LISP
LORE:
A GUIDE TO
PROGRAMMING
THE LISP
MACHINE
Hank Bromley ^g.
LISP LORE: A GUIDE TO
PROGRAMMING THE LISP MACHINE
AT&T
LISP LORE: A GUIDE TO
PROGRAMMING THE LISP MACHINE
by
Hank Bromley
AT&T Bell Laboratories
W
KLUWER ACADEMIC PUBLISHERS
Boston/ Dordrecht/ Lancaster
Distributors for North America:
Kluwer Academic Publishers
101 Philip Drive
Assinippi Park
Norwell, Massachusetts 02061, USA
Distributors for the UK and Ireland:
Kluwer Academic Publishers
MTP Press Limited
Falcon House, Queen Square
Lancaster LAI 1RN, UNITED KINGDOM
Distributors for all other countries:
Kluwer Academic Publishers Group
Distribution Centre
Post Office Box 322
3300 AH Dordrecht, THE NETHERLANDS
Library of Congress Cataloging-in-Publication Data
Bromley, Hank.
Lisp lore.
Includes index.
1. LISP (Computer program language) I. Title.
QA76.73.L23B75 1986 005.133 86-7377
ISBN 0-89838-220-3
Copyright © 1986 by Bell Telephone Laboratories, Incorporated. Portions of this book are copyrighted
by Symbolics, Inc.
All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or
transmitted in any form or by any means, mechanical, photocopying, recording, or otherwise, without
the prior written permission of the publisher, Kluwer Academic Publishers, 101 Philip Drive, Assinippi
Park, Norwell, Massachusetts 02061.
Printed in the United States of America
TABLE OF CONTENTS
LIST OF FIGURES
ix
PREFACE
xi
INTRODUCTION
1
GETTING STARTED ON THE LISP MACHINE
5
1.1 The Keyboard
5
1.2 Typing to a Lisp Listener
8
1.3 Logging In
9
1.4 The FEP
10
1.5 Random Leftovers: the mouse, the monitor, the editor
12
1.6 Problem Set #1
14
WHAT'S A FLAVOR?
17
2.1 Basic Usage
18
2.2 Initial Values for Instance Variables
20
2.3 Mixing Flavors
22
2.4 Combined Methods
23
2.5 Other Ways of Combining Methods
28
2.6 Vanilla-flavor
30
2.7 Fun and Games
31
2.8 Problem Set #2
32
MORE ON NAVIGATING THE LISP MACHINE
43
3.1 The scheduler and processes
43
3.2 Windows
46
3.3 Debugging
50
3.4 Who Does What
52
3.5 The Input Editor and Histories
53
3.6 Fun and Games
54
3.7 Problem Set #3
57
FLOW OF CONTROL
61
4.1 Conditionals
61
4.2 Blocks and Exits
63
4.3 Nonlocal Exits
64
4.4 Iteration
64
5.1
The Nodes and Arcs
5.2
Managing Multiple Windows and Processes
5.3
The Windows and the Mouse
5.4
Fun and Games
5.5
The Program
5.6
Problem Set #5
STREAMS AND FILES
6.1
Streams
6.2
Accessing Files and Directories
6.3
Pathnames
6.4
Fun and Games
6.5
Problem Set #6
TABLE OF CONTENTS
4.5 A Bit More on Working with Macros 71
4.6 Fun and Games 71
4.7 Problem Set #4 73
THE GRAPH EXAMPLE 83
84
85
87
91
91
101
119
119
125
129
135
137
7 THE TREE EXAMPLE 139
7.1 The Nodes and Arcs 139
7.2 The Windows and the Mouse 141
7.3 Fun and Games 143
7.4 The Program 144
7.5 Problem Set #7 155
8 RANDOM USEFUL TOPICS: RESOURCES & SYSTEMS 161
8.1 Resources 161
8.2 Systems 165
8.3 Problem Set #8 170
9 SIGNALING AND HANDLING CONDITIONS 173
9.1 Overview 173
9.2 A Few Examples 174
9.3 More on Handlers 176
9.4 Restart Handlers 178
9.5 More on Proceeding 179
10 THE MOVING ICONS EXAMPLE 183
10.1 General 183
10.2 moving-icons-frame 185
10.3 moving-icons-main-pane 185
TABLE OF CONTENTS
10.4 Messing with the mouse blinker 186
10.5 The :drop-icon method 187
10.6 Setting up the comtab 188
10.7 Getting in the System Menu 188
10.8 The Program 189
10.9 Problem Set #9 194
1 1 MORE ADVANCED USE OF THE EDITOR 199
11.1 Keyboard Macros 199
11.2 Writing New Commands 201
1 1.3 Buffers and Streams 201
1 1.4 Reading from the Mini-buffer 204
11.5 A Real Example 206
11.6 Problem Set #10 207
12 A QUICK LOOK AT "THE NETWORK" 211
12.1 The "Gee- Whiz" Overview 211
12.2 The Beginning of the Real Explanation 213
12.3 The Ethernet 214
12.4Chaosnet 215
12.5 A Bit More on Serial Streams 217
12.6 The Role of the Namespace 2 1 7
12.7 Troubleshooting 219
APPENDIX: BASIC ZMACS COMMANDS 221
INDEX 225
LIST OF FIGURES
1 Flavor tree for lisp-listener 24
2 Structure of combined method 28
3 Transitions among window states 48
PREFACE
This book had its genesis in the following piece of computer mail:
From allegra!joan-b Tue Dec 18 09:15:54 1984
To: solalhjb
Subject: lispm
Hank, I've been talking with Mark Plotnik and Bill Gale about
asking you to conduct a basic course on using the lisp machine.
Mark, for instance, would really like to cover basics like the flavor
system, etc., so he could start doing his own programming without
a lot of trial and error, and Bill and I would be interested in this,
too. I'm quite sure that Mark Jones, Bruce, Eric and Van would
also be really interested. Would you like to do it? Bill has let me
know that if you'd care to set something up, he's free to meet with
us anytime this week or next (although I'll only be here on Wed.
next week) so we can come up with a plan. What do you think?
Joan.
(All the people and computers mentioned above work at AT&T Bell Laboratories,
in Murray Hill, New Jersey.) I agreed, with some trepidation, to try teaching such
a course. It wasn't clear how I was going to explain the lisp machine environment
Xii PREFACE
to a few dozen beginners when at the time I felt I was scarcely able to keep myself
afloat. Particularly since many of the "beginners" had PhD's in computer science
and a decade or two of programming experience. But the need was apparent, and
it sounded like fun to try, so we had a few planning sessions and began class the
next month.
From early January through late March we met once a week, about a dozen times
in all, generally choosing the topic for each session at the conclusion of the previous
one. I spent the last few days before each meeting throwing together lecture notes
and a problem set (typically finishing shortly after the announced class time). By
the end of the course, the students had attained varying levels of expertise. In all
likelihood, the person who learned the most was the instructor; nothing provides
motivation to figure something out like having committed oneself to talking about
After it was over, another co-worker saw the sizable pile of handouts I had gen-
erated and proposed that it would make a good book. He offered to contact a pub-
lisher he had recently deaft with. I was at first skeptical that the informal notes I
had hurriedly concocted would interest a reputable academic publisher, but after
taking another look at the materials that had sprouted, and discussing the matter,
we agreed that quite a few people would find them valuable. I've spent the last few
months filling out and cleaning up the pile, and Presto, change-o. My "set of hand-
outs" is "a book."
There are a number of people who have, in one way or another, consciously or oth-
erwise, helped create this book. Ken Church was instrumental in arranging my
first experience using the lisp machine, and later was responsible for bringing me to
Bell Labs. He also taught a course here, before I came, which laid some of the
groundwork for my own course. Eva Ejerhed, in a rare act of faith, hired me to
work on a lisp machine thousands of miles from the nearest expert assistance,
without my having ever touched one. Joan Bachenko and Bill Gale first suggested I
teach a course at the Labs. Many of my colleagues who served as experimental
subjects by participating in one of the three trials of the course provided useful
comments on the class handouts; among those whose contributions I particularly
recall are Mark Liberman, Jeff Gelbard and Doug Stumberger. Ted Kowalski first
broached the idea of making a book from the handouts, and also — with Sharon
Murrel — supplied lots of assistance with the use of their Monk text formatting
system. Wayne Wolf suggested improvements to my coverage of managing multi-
ple processes. Jon Balgley, of Symbolics, Inc.,* wrote a helpful review of one
Symbolics, Symbolics 3600, Symbolics 3640, Symbolics 3670, and Document Examiner are trade-
marks of Symbolics, Inc.
Zetalisp® is a registered trademark of Symbolics, Inc.
PREFACE
Xlll
version of the manuscript. Valerie Barr introduced herself to the lisp machine by
actually working through an entire draft, making a great many valuable observa-
tions along the way. Mitch Marcus and Osamu Fujimura, my supervision at the
Labs, were most understanding about the amount of time I put into this project.
Carl Harris was an obliging and patient Publisher. Finally, Symbolics, Inc. gra-
ciously allowed me to quote extensively from their copyrighted materials, and
Sheryl Avruch of Symbolics made possible the distribution of a tape to accompany
this book.
I would like to hear about any problems readers have while working their way
through the text. Please don't hesitate to mail me any of your comments or sugges-
tions.
Hank Bromley
December, 1985
computer mail:
US mail:
hjb@mit-mc (arpa)
AT&T Bell Laboratories, room 2D-410
alice 600 Mountain Avenue
research } !sola!hjb (uucp) Murray Hill, NJ 07974
allegra
LISP LORE: A GUIDE TO
PROGRAMMING THE LISP MACHINE
INTRODUCTION
The full 1 1 -volume set of documentation that comes with a Symbolics lisp machine
is understandably intimidating to the novice. "Where do I start?" is an oft-heard
question, and one without a good answer. The eleven volumes provide an excellent
reference medium, but are largely lacking in tutorial material suitable for a
beginner. This book is intended to fill that gap. No claim is made for complete-
ness of coverage — the eleven volumes fulfill that need. My goal is rather to
present a readily grasped introduction to several representative areas of interest,
including enough information to show how easy it is to build useful programs on
the lisp machine. At the end of this course, the student should have a clear enough
picture of what facilities exist on the machine to make effective use of the complete
documentation, instead of being overwhelmed by it.
The desire to cover a broad range of topics, coupled with the necessity of limiting
the amount of text, caused many items to be mentioned or referred to with little or
no explanation. It's always appropriate to look up in the full documentation any-
thing that's confusing. The manuals are perfectly adequate reference materials, as
long as you know what you want to look up. The point in this text is rarely to
explain what some specific function does in isolation — that's what the manuals are
good for. The focus here is on how to integrate the isolated pieces into real appli-
cations, how to find your way around the landscape, how to use the multitudinous
features described in such splendid detail in the 1 1 volumes. The manuals provide
INTRODUCTION
a wonderfully thorough, but static, view of what's in the lisp machine environment;
I've tried to provide a dynamic view of what that environment looks like in action,
or rather in interaction with a human.
The book assumes some background in lisp; the reader is expected to have experi-
ence with some dialect of the language. If you lack such experience, you may want
to do a bit of preparatory study.* This course concentrates on those aspects of lisp
machine lisp ("Zetalisp") which are not found in most dialects, and on the unique
overall programming environment offered by the lisp machine. No experience with
the lisp machine itself is assumed.
Finding an ideal order of presentation for the various topics would be difficult.
Many topics are interdependent, such that knowing either would help in figuring
out the other. Presenting them simultaneously would only confuse matters, so I've
had to settle on one particular linear sequence of topics. It may seem natural to
some readers and bizarre to others. I've tried to identify places where it might be
helpful to look ahead at sections further on in the text, but I'm sure I haven't found
them all, so don't hesitate to engage in a little creative re-ordering if you feel the
urge. One chapter whose position is problematic is that on flavors. Conceptually, it
is probably more difficult than both of the two subsequent chapters {More on Navi-
gating the Lisp Machine and Flow of Control). But I've chosen to put it first
because the flavor system is extremely characteristic of lisp machine programming,
making it important to discuss as soon as possible. The main barrier to mastering
the lisp machine is absorbing its gestalt, much of which is implicit in the flavor sys-
tem; covering that right at the beginning helps to set the tone for what follows. But
if you find flavors a bit much, feel free to look through Navigating and maybe Flow
of Control and come back to it.
I've adopted a rather informal tone for most of the text: people learn better if
they're relaxed. Just let me caution you that "informal" doesn't mean "sloppy."
There are few extra words. Lots of information is present in only one place, and
apparent only if you read carefully. If you get fooled by the informality into think-
ing you can scan half-attentively, you'll miss things.
It must be emphasized that learning to use the lisp machine is more a matter of
learning a way of thinking than of learning a set of specific programming con-
structs. No amount of time spent studiously poring over documentation can yield
the benefits gained from sitting at a console and exploring the environment directly.
Two widely available sources you may find well worth your time are Lisp (2nd edition), Winston and
Horn, Addison- Wesley, 1984, and Structure and Interpretation of Computer Programs, Abelson and
Sussman, MIT Press, 1984.
INTRODUCTION
Time spent examining various parts of the system software with no particular goal
in mind is anything but wasted. Once one has a feel for how things are done, an
overview of how things fit together, the rest will follow easily enough. Most lisp
machine wizards are self-taught; the integrated nature of the environment, and
ready access to the system code, favors those who treat learning the machine as an
interactive game to play.
With that in mind, a word or two of advice on the problem sets. Don't get too
wrapped up in finding the "right answer." Many of the problems are, shall we say,
"challenging;" they require knowledge not found in the text (and in some cases not
even found in the manuals). You will need to investigate, often without knowing
exactly what you're looking for. If the investigation fails to yield immediate results,
I strongly recommend that rather than head straight for my solutions, you continue
to investigate. Stick it out for a while, even if you don't seem to be getting much
closer. You can't learn to speak a foreign language by consulting a dictionary
every time you need a word you don't know — forcing yourself to improvise from
what you do know is the only way. Floundering is an unpleasant but absolutely
necessary part of the process, arguably the only part during which you're really
learning. Similarly, you can't become a lisp wiz just by assiduously studying some-
one else's code. Although seeing how an experienced programmer handles a prob-
lem is certainly useful, it's no substitute for struggling through it yourself. The
problem sets are largely a ruse to get you mucking around on the machine. I don't
really care if you solve them, as long as you come up with some ideas and try them
out with an open mind.
The examples in the text (barring typos) are known to work in Release 6.1 of the
Symbolics software for the 3600 family of machines. Only the "moving icons"
example requires additional support software not included in the text. That
software is available on a cartridge tape, which also contains all the code for the
"graph," "tree," and "moving icons" examples, as it appears here, and all problem
solutions which are too long to reasonably be manually copied from the text.
To order a copy of the tape, write to the following address (you may wish to use
the order form at the back of this book) and include a check for $40 made out to
Symbolics, Inc. Instructions for loading the tape will accompany it.
Software Release
Symbolics, Inc.
1 1 Cambridge Center
Cambridge, MA 02142
Chapter 1
GETTING STARTED ON THE LISP MACHINE
1.1 The Keyboard
Note that there are many keys which don't appear on a standard keyboard. Much
of what you need to know to start using a Lisp Machine boils down to knowing
what the various funny keys do.
Apart from the keys for the standard printing characters (white labels on grey
keys), there are two kinds of special keys. The beige keys with grey labels (shift,
control, meta, super, hyper, and symbol) are all used like the shift key — you hold
them down while striking some other key. These modifier keys may be used singly
or in combination. So "control-meta-K" means type K while holding down control
and meta. There is a standard set of abbreviations for the various modifier keys.
They're all just what you'd expect except that the abbreviation s stands for super
rather than shift. Shift is abbreviated sh.
The beige keys with white labels are special function keys, and are typed like stan-
dard printing characters. That is, "Select-E" means to strike Select and then strike
E. And "Select c-L" means to strike Select and then hold down control and strike
L.
GETTING STARTED ON THE LISP MACHINE
Chapter 1
Use the Help key a lot. The information it supplies depends on the context, but it
usually tells you what sort of input is wanted by the program you're typing to.
You can think of the Lisp Machine as a collection of processes, analogous to the
different users on a time-sharing system. Each process is a program written in lisp
and running in a common environment which all the processes share. A process
typically (but not necessarily) has a window for user interaction. The Select key is
the easiest way to switch among processes. To find out what your options are, type
Select-Help. The display shows you that, among other programs that may be
reached in this way, you can get a lisp listener by typing Select-L, and an editor by
typing Select-E. This list is by no means fixed. Users may add their own programs
to the list quite easily. Here are brief descriptions of the programs that are already
in the select list on a freshly booted lisp machine:
X Common Lisp
C Converse
D Document Examiner
E Editor
F File System Maintenance
I Inspector
L Lisp
M Zmail
N Notifications
P Peek
T Terminal
X Flavor Examiner
a (Common Lisp) lisp listener [X = symbol-sh-L]
convenient way to send and receive messages
from users currently logged-in on other machines
(lisp or otherwise)
a utility for finding and reading online documen-
tation; everything in the 11 -volume manual is
available here
the powerful Zmacs editor, like Emacs plus
much more
various display and maintenance operations on
the file system of the lisp machine or of other
machines
structure editor for displaying and modifying lisp
objects
a (Zetalisp) lisp listener
a comprehensive mail-reading and sending pro-
gram, using many pieces of the Zmacs editor
displays a list of all "notifications" (messages
from running programs) you've received
displays the status of various aspects of the lisp
machine
use the lisp machine as a terminal to log in to
other computers
convenient way to find out about different flavors
(active objects), their message-handlers, and
their state variables.
The Function key, like Select, dispatches off the following keystroke. Function-
Section 1.1 The Keyboard
Help displays a list of the options. The most commonly used are Function-F
("finger"), to find out who's logged in on the various machines, Function-H ("hos-
tat"), for a quick look at the status of all the hosts on the local Chaosnet, and
Function-S to select a different window. The exact behavior of many of the Func-
tion options is controlled by an optional numeric argument; you pass the argument
by pressing one of the number keys after the Function key and before the chosen
letter, e.g., Function-O-S.
The Suspend key generally causes the process you are typing to to enter a "break
loop", that is, the state of its computation is suspended and a fresh read-eval-print
loop is pushed on top of the current control stack. The Resume key will continue
the interrupted computation. Suspend takes effect when it is read, not when it is
typed. If the program isn't bothering to check for keyboard input, pressing
Suspend will do nothing.
c -Suspend does the same thing as Suspend, but always takes effect immediately,
regardless of whether the program is looking for keyboard input.
m-Suspend, when read, forces the process at which you type it into the debugger.
The debugger is another story (see below), but when you're done looking around
you can continue the interrupted computation with Resume.
c-m-Suspend is a combination of c-Suspend and m-Suspend. It immediately forces
the current process into the debugger.
The Abort key is used to tell a program to stop what it's doing. The exact behavior
depends on what program you're typing to. A lisp listener, for instance, will
respond by throwing back to the nearest read-eval-print loop (the top level or an
intervening break loop). Like Suspend, Abort only takes effect when read. If the
program isn't waiting for keyboard input, you need to use c- Abort instead.
m- Abort, when read, throws out of all levels and restarts the top level of the pro-
cess, c-m- Abort has this effect immediately.
The debugger prompt is a small right-pointing arrow. Once you have that, all
kinds of commands are available for moving up and down the stack, getting infor-
mation about the different frames on the stack, restarting execution with or without
modifying arguments and variable values, etc. Try the Help key and see what you
can find out. Besides all the special commands, any normal text you type will be
evaluated by the lisp interpreter.
GETTING STARTED ON THE LISP MACHINE Chapter 1
1.2 Typing to a Lisp Listener
A lisp listener is a window with a lisp interpreter running in it. It reads a lisp
expression from the keyboard, evaluates it, prints the returned value (s), and waits
for another expression. Booting a machine leaves you in a lisp listener. Whenever
you're not in a lisp listener you can get to one by typing Select-L.
While waiting for input, lisp listeners usually display "Command:" as a prompt.
The presence of this prompt indicates that the Command Processor (CP) is active;
it provides a convenient interface to many frequently called lisp functions. (The
name of a CP command won't necessarily be the same as the name of the
corresponding lisp function.) CP commands don't use the same parentheses syntax
as lisp expressions do. You simply type the name of the command (one or more
words) followed by any arguments to the command, and finish with the Return key.
But you needn't type the name of the command in its entirety — all that's required
is enough to uniquely identify which command you mean. The CP command
Help (i.e., type out the letters h, e, 1, p, and hit Return) lists all the defined com-
mands. Pressing the Help key while partway through a command will display a list
of only those commands which match your input thus far. Section 3.2 in volume 1
of the documentation describes all the CP commands present in the software distri-
buted by Symbolics. You can define more of your own. One command which may
be particularly valuable to new users is Show Documentation. You specify
some topic you want looked up in the manuals and it displays a facsimile of that
portion of the documentation on your screen.
You may be wondering how the command processor knows whether you intend
your typein to be interpreted as a CP command or as a lisp expression. If you
begin with a letter, it assumes you're starting a CP command; with a non-
alphabetic initial character it tries to parse your input as a lisp expression. Since
lisp expressions usually begin with a left paren, it guesses correctly most of the
time. But what if you want to evaluate a lisp symbol — if the symbol's name
begins with a letter, the command processor will guess wrong and look for a com-
mand with that name. The solution here is to type a comma before the symbol's
name. The comma has special meaning for the command processor: it forces
whatever follows to be interpreted as a lisp expression, regardless of what the initial
character is.
If you'd like to know about some other features that are available whenever you're
typing to a lisp listener and you don't already feel as though you've seen more than
you can possibly remember, I suggest looking ahead at the section "The Input Edi-
tor and Histories" in chapter 3. It'll make life much easier as you take on the first
few problem sets.
Section 1.2 Typing to a Lisp Listener
1.3 Logging In
To login, you simply use the CP command Login with an argument of your user-
id. In my case, it looks like Login hjb. Alternatively, you could apply the lisp
function "login" to your user-id: (login 'hjb). The effect is the same, but the
former requires less typing (because of the automatic command completion). The
only reason I sometimes use lisp functions when there is an equivalent CP com-
mand is force of habit: the command processor is a fairly new feature, while my
fingers have been typing the lisp functions for years.
It's important to keep in mind the difference between a local login to the lisp
machine, and remote logins to other machines being used as file servers. Local
logins are controlled by a database called the namespace. To login locally with a
certain user-id requires that there be an entry in the namespace for that user-id. It
does not require a password, as there is no internal security on the lisp machine.
Many things on the lisp machine can be done with no one logged in. Some opera-
tions, however, do require that someone be logged in. Modifying the namespace,
for instance, is one of these operations. How, then, you may ask, do you create a
namespace entry for yourself if you can't modify the namespace unless you're
logged in, and you can't log in unless you're in the namespace? One option would
be to log in as someone else so you can create a namespace entry for yourself, and
then log in as yourself. But nothing so underhanded is really necessary. All lisp
machines have a dummy user in the namespace which the system itself uses when it
needs to do something which requires having someone logged in. (This situation
arises most notably while the machine is being booted — no one can log in until it's
finished booting, but it can't finish booting until it does a bunch of things that
require someone being logged in.) The dummy user is typically named "Lisp
Machine", with user-id "Lispm" or "NIL". Whatever it's called on your machine,
you can always use it by typing ( si: login-to-sys-host). This is often a
handy trick to know about. You can now edit the namespace — use the CP com-
mand Edit Namespace Object — and create a user object for yourself. There
is introductory documentation on the namespace and the namespace editor in
chapter 11 of volume 1.
Whenever you log in to a lisp machine, unless you explicitly specify otherwise, it
tries to find your personal initialization file and load it into the lisp environment.
This is a file containing any number of lisp forms which customize the machine for
you. They will typically set some variable values and load some other files. Where
the machine looks for your init file depends on what you specified for your home
host in your namespace entry. If you specified a host running the UNIX*
10 GETTING STARTED ON THE LISP MACHINE Chapter 1
operating system, it will first look for a file named lispm-init .bn in your direc-
tory on that machine. If your home host is a lisp machine, it'll look for the newest
version of a file named lispm-init.bin.
The issue of remote logins arises whenever you try to do something from the lisp
machine on another computer across a network, like read or write a file. If the
remote host is a lisp machine, it won't ask for a password, and your local machine
can take care of establishing the connection with no intervention on your part. But
if the remote host is the sort that believes in security, say a UNIX system, it'll stop
your local machine from doing anything until you provide an appropriate login id
and password. Your local machine will pass the request right along to you. But
it's essentially a matter between you and the remote host — the local machine
doesn't care what username you use on the remote machine, or whether it's one
that exists in the namespace. The local machine is just a messenger in this case. It
will, however, try to be helpful. If you specify in your namespace entry what user-
names you want to use on the various remote hosts, the local machine will try those
first, even if those names are arbitrary nonsense as far as the local machine can tell.
And you can always override the default usernames.
And while we're on remote file systems, there's the question of whether you should
keep your files on a lisp machine or some other sort of file server. It depends on
what sort of set-up you have — how much disk space in what places, how many
users, etc. It's often the case that you'll want to keep as few files as possible on the
lisp machine disks, because the available space would be better utilized by virtual
memory and saved lisp worlds. Files can be kept on any machine you have an eth-
ernet connection to, with little loss of efficiency, so large file systems are effectively
dead weight on a lisp machine. If at all practical it makes more sense to convert
every available megabyte to virtual memory or room for saved worlds, which have
to be on the local disk.
1.4 The FEP
Having looked at the disk label brings up the Front-End Processor, or FEP. The
Fep is a 68000-based computer which handles starting up the main processor of the
lisp machine, and may also become active if the lisp machine enters an illegal state,
whether because of a hardware malfunction or system-software bug. You can tell
if your lisp machine is in the Fep if there's a prompt that looks like this: "Fep>",
and the clock at the lower left of the screen has stopped. For now, there are just
three Fep commands you should know. Continue, which may be shortened to
UNIX is a trademark of AT&T Bell Laboratories.
Section 1.4 The FEP 11
"con", tells the Fep to try having the lisp machine resume exactly where it left off.
If you were thrown into the Fep because of some serious system error, this is not
likely to work — you will probably be thrown right back into the Fep. But not
always. Start, which may be shortened to "st", does a warm boot. It tries to re-
start all of the machine's active processes while preserving the state of the lisp
environment (i.e., function and variable bindings). This is a something of a
kludge.* It can put things into an inconsistent state, and is something of a last
resort, but it is sometimes the only way to get a wedged machine going again, short
of wiping the environment clean, and losing whatever work was in progress. That is
the effect of the Fep command Boot, which may be shortened to "b". Boot does a
cold boot. It clears the machine's memory, reloads the microcode, restores one of
the saved worlds into the virtual memory, and does a start. This is how you get a
fresh machine.
There are times when you may want to get into the Fep so you can do a warm or
cold boot. Perhaps your machine has been used by someone else who has
significantly changed the lisp environment in unfamiliar ways, or who has used up
nearly all your virtual memory. Then you will likely want to do a cold boot. Or
perhaps, as often happens to me, you were playing with some critical part of the
system code (after all, it's written in lisp and is completely accessible), did some-
thing unwise, and now your machine is wedged, responding neither to keyboard
input nor mouse clicks. Then you may wish to resort to a warm boot, and salvage
what you can. So to get into the Fep, the preferred method is to use the CP com-
mand Halt Machine [or evaluate (si: halt)]. That'll do it. But if your
machine isn't responding to the keyboard, typing a command isn't an option. Then
you'll have to use hyper-control- Function. Yes, if you hold down hyper and control
and type Function, your lisp machine will enter the Fep under any conditions other
than hardware failure. This is not the preferred method because the lisp processor
will be rather rudely interrupted and may leave things in an inconsistent state. But
if all else fails, it is the appropriate action.
KLUGE, KLUDGE (Jclooj) noun.
1 . A Rube Goldberg device in hardware or software.
2. A clever programming trick intended to solve a particularly nasty case in an efficient, if not
clear, manner. Often used to repair BUGS. Often verges on being a CROCK.
3. Something that works for the wrong reason.
4. verb. To insert a kluge into a program. "I've kluged this routine to get around that weird bug,
but there's probably a better way." Also "kluge up."
5. A feature that is implemented in a RUDE manner.
(The Hacker's Dictionary, Guy L. Steele, Jr., et at. Harper & Row, Publishers, New York, 1983.)
12 GETTING STARTED ON THE LISP MACHINE Chapter 1
1.5 Random Leftovers: the mouse, the monitor, the editor
A few observations on the mouse. The functions associated with clicking the mouse
buttons are completely context-dependent. It's up to the window the mouse is over
when you click. It is generally the case, though, that the current binding of the
buttons will be documented in the reverse video line near the bottom of the screen.
And it is also generally the case that clicking once on the left button will select the
window the mouse is pointing to, and clicking twice on the right button will get you
to the system menu. The system menu offers many useful operations, such as
creating windows, moving and reshaping existing windows, selecting some of the
programs which are accessible via the Select key, and some which are not, etc.
Play with it. It's a very good habit to keep an eye on the mouse documentation
line.
There is also a lot of other useful information available at the bottom of the screen.
From left to right, we have the date and time; the user-id of the currently logged in
user, if any; the current package (the set of all symbols is partitioned into pack-
ages, to minimize name conflicts — a cold-booted machine starts out in the "user"
package, which is where you'll probably do most of your work at the beginning);
the state of the current process ("Tyi" means awaiting keyboard input); and all
the way on the right, the names of any files that are open for reading or writing, or
a notice of what services have been invoked locally by some other machine. And
underneath the line of text you can sometimes see a few thin horizontal lines.
These are the run bars. The one immediately under the process state goes on when
some process is actively running. The one a bit to the left of that, midway between
the process state and the current package, indicates that you are paging, waiting
for something to be brought in from disk. The other two run bars, which appear
under the current package, you will see less often. They are related to garbage col-
lection.
One last bit of information on the monitor. We have some lisp machines which are
3600 models, and some which are newer 3670s. The two models are quite close in
most respects. One way in which they differ is how the brightness of the display is
controlled. The 3600 monitors have a knob on the bottom side of the console
cabinet, in the front right corner. To adjust the brightness of a 3670, hold down
the Local key and press "B" for brighter or "D" for dimmer. (3640s are like
3670s in this respect.)
The Zmacs editor. The built-in editor commands are multitudinous, and the total
number of available commands is continually growing because it's fairly easy and
very tempting to add new ones. The Appendix lists the most basic commands, but
by far the best way to find out what's around is to get used to using the online
Section 1.5 Random Leftovers: the mouse, the monitor, the editor 13
documentation. Some aspects of the lisp machine can be mastered by reading the
manuals, but the editor is not one of them. Type Help to an editor window, and
type Help again. Start exploring. The most commonly helpful of the help options
are A (Apropos), C (Command), and D (Describe). To get started, use Help -C on
c-X c-F and on c-X c-S. You should also try Help-A on "compile." And one of
the best sources of information on the lisp machine is the m-. command (meta-
period). It prompts for the name of something, then finds the file where whatever
you typed is defined. The something is often a function, but it can also be many
other kinds of lisp objects: a global variable, a flavor, a resource... Two other very
useful features of the editor that you might not run into right away are these: if
you type Suspend, you get a lisp listener which starts at the top of the screen and
grows as you need it. This funny window is called the typeout-window. Resume
returns to the editor. And m-X Dired, which is also invoked by c-X D, is a utility
for editing directories. Call it on some directory and type Help. (Keep in mind
that if it's a lisp machine directory, there's no security to keep you from deleting
absolutely anything.)
14 GETTING STARTED ON THE LISP MACHINE Chapter 1
1.6 Problem Set #1
Questions
This "problem set" is really just a sample programming session, to familiarize you
with basic operations on the lisp machine.
1 . Create a namespace entry for yourself.
2. Log in.
3. Switch to the editor and create an empty buffer for a file in your home direc-
tory named "fact.lisp" (if your home directory is on a lisp machine) or
"fact.l" (if your home directory is on a UNIX machine with a 14 character
limit on file names).
4. Enter the text for a function named "fact" which returns the factorial of its
argument.
5. Compile the function from the editor with c-sh-C, and test it from the
typeout window.
6. When you're satisfied with the function's performance, save the buffer and
compile the resulting file.
7. Cold boot the machine.
8. Log in, and note that the function "fact" is now undefined.
9. Load the compiled version of your file.
10. Run your function successfully.
Section 1.6 Problem Set #1 15
Solutions
1. (si:login-to-sys-host), then Edit Namespace Object. Click on
"create," click on "user," enter your chosen login-id, fill in the required fields
(marked with *), if you wish fill in the optional fields, click on "save," click
on "quit."
2. Login yourid
3. Select-E, c-X c-F fact, lisp (or fact.l)
4. (defun fact (n)
(if (zerop n)
1
(* n (fact (1- n)))) )
5. Type c-sh-C while anywhere inside the text of the function to compile it.
Then hit the Suspend key to get to the typeout window, and evaluate ( fact
5 ). The Resume key returns to the editor window.
6. c-X c-S, Meta-X Compile File (Actually, if you skip the c-X c-S, Compile
File will ask if you want the buffer saved first.)
7. Suspend or Select-L, then Halt Machine. Type b (then Return) to the
Fep prompt.
8. Login yourid, then (fact 5)
9. (load "host : >dir>subdir>f act" ) for a lisp machine,
( load "host : //dir//subdir//f act" ) for a UNIX host.
10. (fact 5)
Chapter 2
WHAT'S A FLAVOR?
(For a more detailed presentation of this material, see Part X, Flavors, in volume 2
of the Symbolics documentation. I have skipped many features of flavors which
you may find useful, and which are fully described there.)
The flavor system is the lisp machine's mechanism for denning and creating active
objects, that is, objects which can receive messages and act on on them. A flavor is
a class of active objects. One such object is called an instance of that flavor.
There are two primary characteristics of a flavor: the set of messages an instance
of that flavor can receive, and the set of state variables an instance of that flavor
has. The state variables are called instance variables. Every object of a given
flavor has the same set of instance variables, but the values of those instance vari-
ables are likely to vary from object to object. And for each message a flavor can
receive, it has a corresponding function to invoke. The function which gets called
to handle a particular message is called the flavor's method for that message. That
method is shared by all instances of the flavor.
So, for instance, the window you see on a freshly booted machine is an instance of
the flavor tv:lisp-listener Like any instance of lisp-listener, it can handle 286
18 WHAT'S A FLAVOR? Chapter 2
different messages (as of the current software). One of the messages it handles is
: expose. Its expose method is a function which makes the lisp-listener visible on
your screen, if it is not already. All lisp-listeners have the same expose method.
One of the instance variables of flavor lisp-listener is exposed-p. All lisp-
listeners have an exposed-p instance variable. If a given lisp-listener happens to be
exposed, perhaps because you just sent it the :expose message, the value of its
exposed-p instance variable will be t. Otherwise it will be nil.
2.1 Basic Usage
Flavors are defined with the defflavor special form. Here is a simple definition of a
flavor named "ship," which might be used in a program for a space wars game.
(defflavor ship
(x-position y-position
x-velocity y-velocity mass)
())
It states that all instances of flavor ship will have five instance variables, as listed.
(The empty list following the instance variables is related to a feature we'll consider
in the section "Mixing Flavors.") Here are two methods for the ship flavor, to han-
dle the messages : speed and : direction.
(def method (ship : speed) ()
( sqrt (+ (expt x-velocity 2)
(expt y-velocity 2))))
(def method (ship : direction) ()
(atan y-velocity x-velocity))
A defmethod looks very much like a defun. It has a function-spec, an argument
list, and a body. The body will be executed in an environment in which the names
of ship's instance variables will refer to the instance variables of the specific ship
object which received the message.
We might also wish to have methods which allow one to examine the values of
ship's instance variables. Like:
(defmethod (ship : x-position) ()
x-position)
Section 2.1 Basic Usage 19
Writing one of these methods for every instance variable would be tedious. For-
tunately there is an option to defflavor that automatically generates such methods
for all the instance variables. There is also an option which causes defflavor to
automatically generate methods for setting the values of all the instance variables.
Their definitions are as though one had done:
(def method (ship : set-x-position) (new-position)
(setq x-position new-position))
To get both of these options, our updated call to defflavor would look like:
(defflavor ship
(x-position y-position
x-velocity y-velocity mass)
()
: gettable-instance-variables
: settable-instance-variables )
To make an instance of flavor ship, we use the make-instance function:*
(setq my-ship (make-instance 'ship))
This will return an object whose printed representation looks like #<SHIP
25564553>. (The funny number will be the virtual memory address, in octal, of
the instance.)
To send a message to an instance, you use the send function. (The effect of send is
in fact identical to that of funcall, but when funcalling an instance send is preferred
for reasons of clarity.) We can now do things like:
(send my-ship : set-x-velocity 1000)
(send my-ship : set-y-velocity 500)
(send my-ship : speed) - 1118.0339
In addition to the instance variables, another very important variable is bound dur-
ing the execution of a method. The value of the variable self will be the instance
object itself. It's often used to send the object another message:
(def method (ship : check-speed) ()
in the special case where the object is a window, you should instead use tv:make-window, which will
perform some necessary bookkeeping operations in addition to calling make-instance for you.
20 WHAT'S A FLAVOR? Chapter 2
(when (> (send self : speed) 3.0e8)
(f error "travel at rates greater than the -
speed of light is not permitted" ) ) )
2.2 Initial Values for Instance Variables
Instances of our ship flavor start out with all their instance variables unbound.
Sending a newly constructed ship the :x-velocity message, for instance, would
result in an unbound-variable error. But there are two ways to arrange for initial
values to be assigned to an instance when it is made. If you have used the
: initable-instance-variables option to defflavor, then you may specify
the initial values in the call to make-instance. So with this defflavor:
(defflavor ship
(x-position y-position
x-velocity y-velocity mass)
()
: gettable-instance-variables
: settable-instance-variables
: initable-instance-variables )
you could use this call to make-instance:
(make-instance 'ship : x-position 30 : y-position -150
:mass 10)
The instance variables mentioned in the call will have the specified initial values.
Instance variables not mentioned will be unbound, as before. Now suppose you
want all instances to have certain initial values for certain instance variables.
Perhaps you want the x-velocity and y-velocity of all new ships to be 0. You could
specify so in every call to make-instance. But there is an easier way. You can
specify in the defflavor what initial value you wish the instance variables to have.
Here's our next version of the defflavor for ship:
(defflavor ship (x-position
y-position
(x-velocity 0)
(y-velocity 0)
mass)
()
: gettable-instance-variables
Section 2.2 Initial Values for Instance Variables 21
: settable-instance-variables
: initable-instance-variables )
Now all ships will start out with x- and y- velocities of 0 — unless you specify oth-
erwise in the make-instance. An initial value specified in make-instance will over-
ride any default initial values given in the defflavor.
Here is a slightly more complex example, taken from p. 427 of the flavor documen-
tation:
(defvar *def ault-x-velocity* 2.0)
(defvar *default-y-velocity* 3.0)
(defflavor ship ( (x-position 0.0)
( y-position 0.0)
(x-velocity *def ault-x-velocity* )
(y-velocity *def ault-y-velocity* )
mass )
()
: gettable-instance-variables
: settable-instance-variables
: initable-instance-variables )
(setq another-ship
(make-instance "ship :x-position 3.4))
The values of the new ship's instance variables will be 3.4 for x-position (the
make-instance specification overrides the default of 0.0), 0.0 for y-position (the
default), 2.0 for x-velocity and 3.0 for y-velocity (the values of the two global vari-
ables), and mass will be unbound.
It's useful to know that describe, a function which tries to print helpful information
about its argument, no matter what it is, lists the values of all the instance vari-
ables when applied to an instance:
(describe another-ship) would print
#<SHIP 40113625>, an object of flavor SHIP,
has instance variable values :
X-POSITION: 3.4
Y-POSITION: 0.0
X-VELOCITY: 2.0
22 WHAT'S A FLAVOR? Chapter 2
Y-VELOCITY: 3.0
MASS : unbound
2.3 Mixing Flavors
The real power of the flavor system lies in its facility for producing new flavors by
combining existing ones. Suppose we wished to define an "asteroid" object. It
would need much of the same functionality as the ship flavor. In fact, all of ship's
instance variables and methods would be appropriate. But we do want to have two
distinct kinds of object, because we might wish to add more functionality to ship
and asteroid which would not be shared. Ship, for instance, could use an instance
variable for its engine power, or we might want to give each ship a name. And for
each asteroid we might want to characterize its composition (perhaps part of the
game requires replenishing resources by mining asteroids).
One way to handle the situation would be to duplicate the lisp code for the common
functionality in both flavors. Such duplication would clearly be wasteful, and the
program would become far more difficult to maintain — any modifications would
have to be repeated in both places. A better approach would be to isolate the com-
mon functionality and make it a flavor in itself. We can call it "moving-object."
Now the ship and asteroid flavors can be built on moving-object. We just need to
specify the added functionality each has beyond that provided by moving-object.
The defflavor for moving-object can be exactly like our existing defflavor for ship.
The new ship defflavor will have moving-object specified in its list of component
flavors, which up until now has been an empty list.
(defflavor ship (engine-power name)
(moving-object)
: gettable-instance-variables
: initable-instance-variables )
And asteroid:
(defflavor asteroid (percent-iron)
(moving-object)
: gettable-instance-variables
: initable-instance-variables )
Ship and asteroid both inherit all of moving-object's instance variables (including
their default values) and all of its methods. They are each specializations of the
abstract type moving-object. And the specialization could continue. We could
Section 2.3 Mixing Flavors 23
define a "ship-with-passengers" flavor, built on ship, with an added instance vari-
able passengers, and added methods for :add-passenger and : remove-
passenger.
A flavor is not limited to having only one component flavor — it may have any
number. So the set of components for a given flavor is actually a tree, consisting of
all the flavor's direct components, and all of their direct components, and so on.
Figure 1 shows the tree for the flavor tv:lisp-listener. (All are in the tv package,
unless otherwise noted.) As you can see, it can get quite elaborate. tv:lisp-listener
has 26 component flavors in all. Of the 287 handlers I mentioned earlier that
tv:lisp-listener has, only one of them is locally defined in tv:lisp-listener. The rest
are all inherited, about 150 from tvrsheet alone, and another 50 or so from
tv:stream-mixin.
2.4 Combined Methods
Saying simply that a flavor inherits all the methods of its components sweeps an
important issue under the rug. What happens if more than one of its components
define methods for the same message? Which gets used? It depends on the order-
ing of the component flavors. As it says on p. 432 of the documentation, "The tree
of flavors is turned into an ordered list by performing a top-down, depth-first walk
of the tree, including nonterminal nodes before the subtrees they head, and elim-
inating duplicates." So the for tv:lisp-listener, the ordered list starts with: lisp-
listener, listener-mixin, listener-mixin-internal, process-mixin, window...
If more than one component flavor defines a method for a given message, with the
kind of methods we have seen so far, the one which appears first on the list is taken
as the combined flavor's method for that message. In particular, this means that
any methods (again, of the type we have seen so far) defined locally in the new
flavor will supersede all methods (for the same message) defined in any of the com-
ponent flavors, since the new flavor is first on the ordered list. For example, the
flavors tv.lisp-listener and tv:essential-window both define methods for the message
: lisp-listener-p. The one in tv:essential-window always returns nil. The
one in tv:lisp-listener always returns t. So a window without tv:lisp-listener mixed
in will answer the : lisp-listener-p message with nil. But a lisp-listener
will answer t, because its local method overrides tv:essential-window's.
[There is an exception to the top-down depth-first rule. If you're
already confused, skip these two paragraphs for now and come
back later. If not, it's time to learn about the : included-
flavors option to defflavor. Suppose you're defining a flavor
24
WHAT'S A FLAVOR?
Chapter 2
LISP-LISTENER
LISTENER-MIXIN
LISTENER-
MIXIN-
INTERNAL
/
PROCESS STREAM
BORDERS
MIXIN
MIXIN-
MIXIN
WINDOW
SELECT-
MIXIN
MINIMUM-
WINDOW
LABEL-
MIXIN
GRAPHICS-
MIXIN
ESSENTIAL-
EXPOSE
ESSENTIAL-
SET-EDGES
ESSENTIAL-
WINDOW
ESSENTIAL-
LABEL-
MIXIN
ESSENTIAL-
ACTIVATE
ESSENTIAL-
MOUSE
MARGIN-HACKER-MIXIN
Sh INTERACTIVE -STREAM
ShLINE-OUTPUT
STREAM-MIXIN
ST.BIDIRECTION-
STREAM
ShCHARACTER
STREAM
SHEET
ShOUTPUT-
STREAM
ShSTREAM
SL-INPUT-STREAM
Figure 1. Flavor tree for lisp-listener
Section 2.4 Combined Methods 25
(call it my-flavor) which is intended to be used as a component for
other flavors (call one of them user-flavor). And suppose that for
the methods defined in my-flavor to work properly, it's necessary
that some other flavor (call it needed-flavor) also be mixed into the
user-flavor. And suppose further that needed-flavor is a rather
basic flavor, and so should appear towards the end of user-flavor's
ordered list. You could guarantee that needed-flavor always be
present whenever my-flavor is present by making it a component of
my-flavor. But then it would appear just behind my-flavor in
user-flavor's ordered list of components. (Possibly closer to the
front if some other component flavor uses it, but certainly no
further back.) And some of needed-flavor's methods might over-
ride methods of other flavors which were really intended to over-
ride needed-flavor's methods, expecting to find needed-flavor near
the end of the list. The solution here is the rincluded-
flavors option. My-flavor lists needed-flavor as an included
flavor. The effect is that when user-flavor uses my-flavor, needed-
flavor will appear in the ordered list immediately after my-flavor
only if no other flavors in user-flavor have needed-flavor as a com-
ponent. If some other flavor does — and the expected case is that
somebody near the end of the list will — then needed-flavor will
appear there, as though my-flavor never mentioned it.
An example: look back at the lisp-listener tree, and note the posi-
tion of process-mixin. It will be the fourth flavor in lisp-listener's
ordered list. The defflavor for process-mixin specifies that
essential-window is an "included-flavor," because for process-mixin
to work properly, any flavor which uses it must also use essential-
window. But if process-mixin specified essential- window as a nor-
mal component, essential-window would be brought all the way
from its current position near the end of the list to fifth position,
just behind process-mixin. Worse yet, sheet would be pulled from
last to sixth, because it is a component (normal) of essential-
window. But many of sheet's methods are supposed to be overrid-
den by the other flavors, e.g., select-mixin. If sheet were pulled in
front of select-mixin, the lisp-listener would never see many of the
select-mixin methods, and it wouldn't behave properly at all. J
As I've hinted, there are more kinds of methods than we have so far seen. All our
methods have been what are called "primary" methods, and by default, when there
is more than one primary method for the same message in the ordered list of com-
ponent flavors, the one which appears first overrides all others. But sometimes you
26 WHAT'S A FLAVOR? Chapter 2
don't want to completely override the inherited primary method; sometimes you
would like to specify something to be done in addition to the action of the inherited
method rather than instead of. Then you would define a : before or an rafter
method, often called before and after daemons.
Here's how it works. Suppose I did the following defmethod:
(def method (asteroid rafter : speed) ()
(do-something-or-other ) )
Asteroid already has a primary : speed method, inherited from moving-object.
Once this new rafter : speed method is defined, asteroid will have a "com-
bined method" for r speed, consisting of a call to the moving-object primary
method followed by a call to the asteroid r after method. Any number of flavors
in the ordered list of components may provide daemons. They will all be included
in the resulting combined method. To quote from the documentation again,
"Before-daemons are called in the order that flavors are combined, while after-
daemons are called in the reverse order. In other words, if you build bar on top of
foo, then bar's before-daemons will run before any of those in foo, and bar's after-
daemons will run after any of those in foo." The primary method which appears
first in the list will be called after all the before daemons (even if some of the
before daemons appear later in the list than the primary method) and before all the
after daemons.
The value returned by a combined method is exactly the value returned by the pri-
mary method — before and after daemons are executed only for side effect, i.e.,
their return values are ignored. It is allowable to have before and after daemons
for a message which has no primary method; in such a case the combined method
will return nil.
Before and after daemons provide a lot of flexibility (perhaps more than you'd like
to have just yet), but sometimes not enough. Fairly frequently a situation demands
altering the context in which a primary method runs. A typical case would be
binding a special variable to some value around the execution of the primary
method, or putting the primary method into an unwind-protect or inside a catch.*
Or deciding in some cases to skip the primary method altogether. Before and after
daemons are unable to do any of these. The kind of method which can is called a
whopper.
Whoppers are best explained by example. Here are three which handle the cases I
* If unwind-protect or catch are unfamiliar, you might want to look them up in volume 2.
Section 2.4 Combined Methods 27
just listed. To understand them you'll need to know that continues hopper is a
system-provided function which calls the regular (non-whopper) methods for this
message.
(def whopper (some-flavor : some-message ) (argl arg2 )
(let ((some-special-variable temporary-value))
(continue-whopper argl arg2 ) ) )
(def whopper (some-flavor : some-message ) (argl arg2 )
( unwind-protect
(progn (setup)
(continue-whopper argl arg2 ) )
(cleanup) ) )
(def whopper (some-flavor : some-message ) (argl arg2 )
(unless (some-special-test argl)
(continue-whopper argl arg2 ) ) )
Unlike before and after daemons, whoppers have control over the value returned by
the combined method. They most commonly just pass up the value returned by
continue-whopper (which will be whatever the primary method returns, as before),
but they needn't. I could, for instance, do this:
(def whopper (doubling-mixin : calculate) (argl arg2)
(* 2 (continue-whopper argl arg2)))
And since continue-whopper is just a function like any other, there's no reason you
couldn't do something like this:
(def whopper (doubling-mixin-of-another-sort : some-message)
(argl arg2 )
(continue-whopper argl arg2 )
(continue-whopper argl arg2 ) )
(def whopper (yet-another-doubling-mixin : some-message)
(argl arg2 )
(continue-whopper argl (continue-whopper argl arg2 ) ) )
One point about ordering needs to be clarified. A whopper surrounds not just the
primary method, but all the before and after daemons, too. So suppose flavor "out"
is built on top of "in," and both out and in have a whopper, a before daemon, an
after daemon, and a primary method for message : mumble. Out's combined
28 WHAT'S A FLAVOR? Chapter 2
method for : mumble would look like Figure 2.
out-before
in-before
out-whopper in-whopper out-primary
in-after
out-after
Figure 2. Structure of combined method
There is another kind of construct called a wrapper. This was the predecessor of the
whopper, but now that the whopper exists, which is easier to use, there is seldom
any need to use wrappers.
2.5 Other Ways of Combining Methods
All that I said in the previous section applied only to the default style of combining
methods, called the : daemon type. There are actually about a dozen different
types of method combination. Moreover, users can define additional types (with
some effort). Before despairing, though, note that at least 90% of the time the
: daemon type of method combination is used. All of the built-in types are dis-
cussed in chapter 53 of Symbolics volume 2, "Method Combination." I will
describe one of them, the : or type, both because it is a relatively simple example
of what can be done, and because I've actually seen it used.
First, in order to specify that you wish to use a type of method combination other
than daemon, you use the : method-combination option to defflavor. Here is a
Section 2.5 Other Ways of Combining Methods 29
defflavor pulled from the source code for the window system:
(DEFFLAVOR ESSENTIAL-MOUSE () ()
( : INCLUDED-FLAVORS ESSENTIAL-WINDOW)
(: METHOD-COMBINATION ( :OR : BASE-FLAVOR-LAST : MOUSE-CLICK) ) )
essential-mouse is one of the flavors that appears in the lisp-listener tree given
above. It's the mixin that enables a window to interact properly with the mouse.
As you can see, this flavor has no instance variables and no component flavors. It
has essential-window as an : included-f lavor, though that has no bearing on
the immediate issue of non-standard method combination. The : method-
combination option is what concerns us. It says that the : mouse-click
methods for all flavors built on this flavor should use :or style combination, and
that the order of combination should be base flavor (essential-mouse) last.
So what's :or combination, and what does base-flavor-last mean? In :or combi-
nation, all the methods appearing in the ordered list of component flavors are col-
lected, and each is called in turn. If a particular method returns a non-nil value,
the remaining methods are skipped. Otherwise (the method returned nil), the
next one is called. We just step through all the methods, stopping as soon as one
returns a non-nil value. Base-flavor-last means that the essential-mouse method for
: mouse-click will be the last one to be tried, i.e., the methods will be collected
and tried in the exact same order as their flavors appear in the ordered list of com-
ponents. The opposite ordering may be specified with :base-f lavor-f irst.
Suppose I define a flavor built (directly or indirectly) on essential-mouse, and give
my flavor a : mouse-click method. Then whenever the : mouse-click mes-
sage is sent to an instance of my flavor, my : mouse-click method will be called.
If my method returns anything other than nil, no other : mouse-click
methods will be called. If my method returns nil, then any .-mouse -click
methods defined by flavors which are components of my flavor will get a chance. If
the only other : mouse-click method is essential-mouse's, or if all the others
return nil, then the essential-mouse method for : mouse-click will be called.
The : mouse-click message is sent to the window under the mouse blinker
whenever you press one of the mouse buttons. (The mouse process takes care of
sending the message — you don't need to worry about it.) If you want to do some-
thing special when the buttons are pressed, you simply need to define an appropri-
ate : mouse-click method. Now there are six different kinds of button presses
(three different buttons, and single or double clicks on each button). Maybe you
have something you want to do if there is a single click on the left button, but not
30 WHAT'S A FLAVOR? Chapter 2
if there is a double click on the middle button. One of the arguments to the
method will tell which kind of button press there was, so your method should test
the argument, and if it's the kind of button press you want to handle (single left),
do whatever you had in mind, and return something non-nil, so that the other
: mouse-click methods won't be called and possibly do something else with the
single left click, interfering with your action. If it's some button press that you
don't care about (double middle), return nil so that the other methods will have a
chance to handle it.
Here's essential-mouse's method for : mouse-click, which is called if no one else
handles the button press:
(DEFMETHOD (ESSENTIAL-MOUSE : MOUSE-CLICK) (BUTTONS X Y)
(COND ((AND (= BUTTONS #\MOUSE-L-1)
(NEQ SELF SELECTED-WINDOW)
(GET-HANDLER-FOR SELF ': SELECT))
(MOUSE-SELECT SELF)
(SEND-IF-HANDLES SELF : FORCE-KBD- INPUT
x ( : MOUSE- BUTTON , BUTTONS , SELF ,X ,Y)
T))
( (OPERATION-HANDLED-P SELF : FORCE-KBD- INPUT)
(SEND SELF : FORCE-KBD-INPUT
*( : MOUSE-BUTTON , BUTTONS , SELF ,X ,Y)
T))
((= BUTTONS #\MOUSE-R-1)
(MOUSE-CALL- SYSTEM-MENU) )
(T
(BEEP) ) )
T)
You don't need to understand all the details to write your own : mouse-click
methods. All you need to understand is the general format of testing the "buttons"
argument and choosing some action accordingly.
2.6 Vanilla-flavor
shvanilla-flavor is the generic flavor on which all other flavors are built. Even if
your defflavor specifies no components, your flavor will still have vanilla-flavor
mixed in because the flavor system does it automatically (unless you explicitly
instruct otherwise with the :no-vanilla-f lavor option to defflavor). But
Section 2.6 Vanilla-flavor 31
don't complain, because vanilla-flavor is very handy. It provides several extremely
important methods. The : print-self method is called whenever an instance is
to be printed. (The representation of the first ship instance we made, #<SHIP
25564553>, was actually printed on my monitor by ship's : print-self
method, inherited from vanilla-flavor.) The : describe method is used by the
describe function. (The example shown earlier, where the describe function listed
all of a particular ship's instance variables, was printed by ship's : describe
method, also inherited from vanilla-flavor.) The :which-operations method
returns a list of all the messages handled by the object. The :get-handler-
f or method takes one argument, the name of a message, and returns the function
object which is the instance's handler for that message.
See chapter 52 in the flavor documentation for the remaining vanilla-flavor
methods.
2.7 Fun and Games
And from The Hacker's Dictionary, Guy L. Steele, Jr., et ah
FLAVOR noun.
1. Variety, type, kind. "Emacs commands come in two flavors: single-character
and named." "These lights come in two flavors: big red ones and small green
ones." See VANILLA.
2. The attribute that causes something to be FLAVORFUL. Usually used in the
phrase "yields additional flavor." Example: "This feature yields additional
flavor by allowing one to print text either right-side-up or upside down."
VANILLA adjective.
Standard, usual, of ordinary FLAVOR. "It's just a vanilla terminal; it
doesn't have any interesting FEATURES." When used of food, this term
very often does not mean that the food is flavored with vanilla extract! For
example, "vanilla-flavored wonton soup" (or simply "vanilla wonton soup")
means ordinary wonton soup, as opposed to hot-and-sour wonton soup.
This word differs from CANONICAL in that the latter means "the thing you
always use (or the way you always do it) unless you have some strong reason
to do otherwise," whereas "vanilla" simply means "ordinary." For example,
when MIT hackers go to Colleen's Chinese Cuisine, hot-and-sour wonton
soup is the canonical wonton soup to get (because that is what most of them
usually order) even though it isn't the vanilla wonton soup.
32 WHAT'S A FLAVOR? Chapter 2
2.8 Problem Set #2
Questions
Part I
1. Define a function of four arguments that draws a square. The args are the
window, the x and y coords of the square's center, and the size (length of
each side).
2. Define a flavor of window which handles a : draw- square message by call-
ing your draw-square function. Then create a window of that flavor, being
sure to keep a pointer to it, and verify that the : draw-square message
works.
3. Make the size argument to the method optional, defaulting to the value of a
global variable *square-size*. Make the default size 100 pixels.
4. Arrange it so that clicking left on the mouse while over your window draws a
square centered on the mouse position, using the default size.
5. A. Try these out — they temporarily bind the default size to 50 instead of
100, and then draw a square using the default size.
(let ( (*square-size* 50))
(send w : draw-square 150 150))
(let ( (*square-size* 50))
( process-sleep 300 ) ) click left over my-window during the sleep
Why doesn't the second work? Why isn't the binding of *square-size*
to 50 being seen?
B. There is special form called let-globally which will get around this
problem. Look it up in the manual and use it with a process-sleep so
that clicking left over the window will make a square whose size is con-
trolled by the let-globally.
Making the default size an instance variable will get around the problem
addressed in (5), and has several other advantages. We no longer clutter
things up with an extra global variable. More important, with the default
size an instance variable, it's possible for each instance of my-window to
simultaneously have a different value for default-square-size.
Redefine your flavor of window (and make a new instance) so the following
works:
Section 2.8 Problem Set #2 33
(let ((old-size (send w :def ault-square-size) ) )
(send w : set-default-square-size 50)
(process-sleep 300)
(send w : set-default-square-size old-size))
7. What would happen if, during the process-sleep in the let in problem (6), I
typed c-Abort? The reset of default-square-size back to its previ-
ous value would never happen. The let, which is intended to be without
side-effect, would have permanently changed the value of the instance vari-
able.
Add something to the let in problem (6) so that it will be guaranteed to reset
default-square-size to its previous value.
8. Without duplicating any code, define a new flavor of window, doubling-
window, which behaves just like the window you've already defined, except
that the squares it draws are twice as big as you ask for. That is, if you click
left the square will be twice the size specified by the window's default-
square-size instance variable, and if you explicitly send the : draw-
square message the square will be twice the size specified by the third argu-
ment to the message.
Part II
Not too long ago, I had to track down some bugs in the speech editor I was work-
ing on, related to the ordering of the components of one of my flavors. I had
switched the order of two components to fix one bug, and apparently introduced
some new ones. At least, the program was misbehaving in a way it previously
hadn't, and the only relevant change I'd made was the component reordering. It
was a sort of misbehavior that would have been difficult to debug by interrupting
the program while it was doing the wrong thing, examining the state of the world,
and working back to see which message-handler was responsible for the new
behavior. So I instead decided to find out what messages would be handled
differently with the new component ordering, and work forward to see which of
those changed handlers could be causing the odd behavior.
The Flavor Examiner (Select-X) has a facility for listing all the message-handlers
of a given flavor, together with the name of the flavor the handler was inherited
from. So all I had to do was list all the handlers for my two flavors, and compare
them. If only one of the two handled a particular message, it wouldn't matter in
which order the flavors were mixed in, since I would get the same handler either
way. And if they both handled a message, but handled it with the same inherited
method from a common component, the order of combination still wouldn't matter.
Any messages, however, which were handled by both flavors but with different
34 WHAT'S A FLAVOR? Chapter 2
methods, would be likely suspects. Which handler my combined flavor had would
indeed depend on the order of combination.
It would have been a simple task, except that the two flavors involved each had
between 190 and 200 handlers. Both included the flavor tv: window, which has 194
handlers. Nearly all of the handlers I had to wade through were internal to
tvrwindow (mainly from the flavors tvrsheet and tv:stream-mixin) , and shared by my
two flavors. There were only a handful of suspects, and once I found them it did
not take too long to realize that my mistake had been in using tv:window-pane
where I should have used tv:pane-mixin. The bugs went away. But it was a far
more painful experience than it would have been if we had a few simple utilities for
filtering the lists of handlers. This portion of the problem set asks you to write a
few such facilities.
1 . Write a function of two arguments, to be called on two instantiated objects of
different flavors. It should return a list with four sublists: a list of handlers
in flavor- 1 and not in flavor-2, a list of handlers in flavor-2 and not in flavor-
1, a list of handlers in both flavors, and a list of pairs of corresponding
handlers, for messages handled by both flavors but with different handlers.
2. That's already enough to be useful, but often a flavor is not instantiated, and
sometimes may not even be instantiable without further mixins. The function
you've defined won't work on such flavors. Write a similar function which
will take as arguments the names of two flavors.
3. The answer to (1), and depending on how you did it, quite possibly the
answer to (2), are subject to a bug of sorts. They may decide under certain
conditions that two handlers are different, when for all practical purposes
they are not. Consider the following:*
FOO (foo)
1. interjection. Term of disgust. For greater emphasis, one says MOBY FOO (see MOBY).
2. noun. The first metasyntactic variable. When you have to invent an arbitrary temporary
name for something for the sake of exposition, FOO is usually used. If you need a second one,
BAR or BAZ is usually used; there is a slight preference at MIT for bar and at Stanford for
baz. (It was probably at Stanford that bar was corrupted to baz. Clearly, bar was the origi-
nal, for the concatenation FOOBAR is widely used also, and this in turn can be traced to the
obscene acronym "FUBAR" that arose in the armed forces during World War II.)
Words such as "foo" are called "metasyntactic variables" because, just as a mathematical
variable stands for some number, so "foo" always stands for the real name of the thing under
discussion. A hacker avoids using "foo" as the real name of anything. Indeed, a standard
convention is that any file with "foo" in its name is temporary and can be deleted on sight.
BAR
The second metasyntactic variable, after FOO. If a hacker needs to invent exactly two names
Section 2.8 Problem Set #2 35
(def flavor foo ( ) ( ) )
(def flavor bar ( ) ( ) )
(def method (foo : silly-message ) () nil)
(def method (bar : before : silly-message) () nil)
(def flavor flav-1 ( )
(foo bar) )
(def flavor flav-2 ( )
( foo bar ) )
Flav-1 and flav-2 each have a combined method for the message : silly-
message, and they use the same components for their respective combined
methods. I would like these two combined methods to be considered the
same, and fall into the third of our four sublists. But your functions may be
putting such pairs of methods into the fourth sublist. If so, modify one or
both of your answers to (1) and (2) to re-classify these methods accordingly.
Of all your answers to questions (l)-(3), select the one that seems to be the
most useful, and merge it into the Flavor Examiner.
for things, he almost always picks the names "foo" and "bar."
(The Hacker's Dictionary, Guy L. Steele, Jr., et al)
36 WHAT'S A FLAVOR? Chapter 2
Hints
Part I
1. Your function should either send the window the : draw- line message four
times, or the : draw- lines message once. You can find out about the
arguments to the : draw-line and : draw-lines messages by looking
them up in the index to volume 7.
2. Your flavor of window should be built on tv.window. The : draw- square
method will need to use the self variable.
Using the window brings up a somewhat subtle issue. At times you'll need
the lisp listener exposed so you can type commands. And at times you'll need
your new window exposed so it can display the squares being drawn. You
could, then, alternate between the two windows, but that becomes awkward.
Far better is to position and shape the two windows such that they can both
be exposed simultaneously. One procedure would be to first narrow the lisp
listener down to the left half of the screen (choose Edit Screen from the sys-
tem menu, then Move Single), then make your window with the following
form, choosing an area with the mouse that doesn't overlap the narrowed lisp
listener:
(setq w (tv: make -window 'my-window :edges-from : mouse
:expose-p t ) )
Now you're ready to try drawing squares, with (send w : draw-square
... ).
3. The global variable should be defined with defvar or def const, and initialized
to 100 in the call to defvar/const. The redefined method should include in its
arglist "^optional (size *square-size* )".
4. The window needs a : mouse-click method. Recall that :mouse-click
methods use : or combination, so your method should return t if it handles
the click, and nil otherwise.
5. A. Think about processes.
B. let-globally looks just like a let.
6. The flavor will have an instance variable named default-square-size,
and you'll need to use the rsettable-instance-variables option.
The new : draw- square method will have to access the instance variable.
7. Use unwind -protect
Section 2.8 Problem Set #2 37
8. Your new flavor should be built on the old one, and should have a whopper
for : draw- square.
Part II
1. You can get a list of all the messages an object handles with :which-
operations. And given a specific message, you can get the handler for it
with :get-handler-for.
2. The Flavor Examiner finds the methods from the flavor name, without having
an instantiated object. Find out how. As a simpler and nearly as useful
alternative, note that the editor command M-x List Combined Methods
(among others) can do the same thing as long as the flavor has been instan-
tiated at least once, and track down what it does. (Try meta-. on com-list-
combined-methods.) This won't help for uninstantiated flavors, but does
remove the need to have a pointer to an actual instance in cases where the
flavor has been instantiated.
3. If you're modifying your answer to (1), you're probably dealing with com-
piled function objects. Notice that the describe function, when applied to the
compiled function object for a combined method, prints the necessary infor-
mation under the title extra info, after :f definition-location-
hints. Find out how.
If you're modifying your answer to (2) you probably already have a data
structure containing the necessary information, and you just need to make
use of that information to re-classify the handlers.
4. The source code for the Flavor Examiner is in the file "sys: window; flavex."
38
WHAT'S A FLAVOR?
Chapter 2
Solutions
Part I
x half-size) )
y half-size) ) )
1. (defun draw-square (window x y size)
(let* ((half-size (// size 2))
(xO (- x half-size)) (x1 (+
(yO (- y half-size) ) (y1 (+
(send window :draw-line xO yO xO y1 )
(send window : draw- line xO y1 x1 y1 )
(send window : draw-line x1 y1 x1 yO )
(send window :draw-line x1 yO xO yO ) ) )
(defun draw-square (window x y size)
(let* ((half-size ( // size 2))
(xO (- x half-size)) (x1 (+ x half-size))
(yO (- y half-size)) (y1 (+ y half-size)))
(send window : draw-lines tv:alu-ior
xO yO xO y1 x1 y1 x1 yO xO yO ) ) )
2. (def flavor my-window ( )
(tv: window) )
(def method (my-window : draw-square) (x y size)
(draw-square self x y size))
3. (defvar *square-size* 100)
(def method (my-window : draw- square)
(x y &.optional (size *square-size*
(draw-square self x y size))
4. (def method (my-window : mouse-click
(cond ((= buttons #\mouse-l-1)
(send self : draw-square (
(buttons x y,
5. A.
t)
(t nil)))
x tv: lef t-margin-size )
(- y tv: top-margin-size) )
prevent other .mouse-click methods
from being called (:or combination)
allow other kinds of clicks to fall through
It doesn't work because the : mouse-click method is called from
the mouse process, and different processes each have their own variable
binding stacks. The mouse process can see the global value of
*square-size* (100) which was set by the defvar, but not the let bind-
ing (50) which is in the binding stack of your lisp listener. (There is
Section 2.8 Problem Set #2 39
more information on processes in the next chapter.)
B. (let-globally ( ( *square-size* 50))
(process-sleep 300))
6. (def flavor my-window
( (default-square-size 100 ) )
(tv: window)
: settable-instance-variables )
(def method (my-window : draw-square ) (x y ^optional size)
; ; can't default the optional arg because environment
; ; isn't set up yet - use "or" in body
(draw-square self x y (or size default-square-size)))
7. (let ((old-size (send w :def ault-square-size) ) )
( unwind-protect
(progn (send w : set-default-square-size 50)
(process-sleep 300))
(send w : set-default-square-size old-size)))
kwc-letf, a macro written by Ken Church, does just this sort of thing, and
looks a lot nicer. (Its name was chosen to distinguish it from letf, a special
form provided by Symbolics which has similar, but slightly different, effects.)
The syntax is like that of let, except in place of the names of local variables
to be bound, kwc-letf accepts any reference which can be understood by setf.
It arranges for that reference to return the specified value if called within the
kwc-letf, and for the old value to be restored upon exiting. In our case, it
would be used like this:
(kwc-letf (((send w :def ault-square-size ) 50))
(process-sleep 300))
This produces identical compiled code, but makes the intent much clearer —
you can see that I just want to evaluate the process-sleep in a context where
(send w :def ault-square-size) would return 50. The call to kwc-
letf macroexpands into:
(LET ((#:G0531 (SEND W : DEFAULT-SQUARE-SIZE) ) )
(UNWIND-PROTECT
(PROGN (SEND W ': SET-DEFAULT-SQUARE-SIZE 50)
(PROCESS-SLEEP 300))
(PROGN (SEND W ': SET-DEFAULT-SQUARE-SIZE #:G0531))))
40 WHAT'S A FLAVOR? Chapter 2
8. (def flavor doubling -window ()
(my-window) )
(defwhopper (doubling-window : draw-square) (x y ^optional size)
(continue -whopper x y (if size (* 2 size)
(* 2 default-square-size))))
An alternative whopper would be:
(defwhopper (doubling-window :draw-square) (x y &.optional size)
(let ((default-square-size (* 2 default-square-size)))
(continue-whopper x y (and size (* 2 size)))))
Part II
1 . Here is a simple-minded implementation:
(defun sort-handlers ( object- 1 object-2)
(loop
for message in (union (send object- 1 :which-operations)
(send object-2 :which-operations) )
for handler- 1 = (send object- 1 :get-handler-for message)
for handler-2 = (send object-2 :get-handler-for message)
when (not handler-2) collect handler- 1 into only-1
else when (not handler-1) collect handler-2 into only-2
else when (eq handler-1 handler-2) collect handler-1
into both-and-same
else collect (cons handler-1 handler-2)
into both-and-dif f erent
finally (return (list only-1 only-2
both-and-same both-and-dif f erent) ) ) )
2. A partial answer: everything you need to know about a flavor is in the list
returned by (si: examiner-compute-magic-list (get flavor -name
'si : flavor) ). The extraction of the info is done by the method
:compute-all-handlers-once of the flavor flavex:flavor. As for M-x
List Combined Methods, the crucial function is zwei:find-combined-methods
3. To follow up the describe hint, this is what describe uses:
(cdr (assq :fdef inition-location-hints
( si : cca-extra-inf o
(si:compiled-function-cca function-obj) ) ) )
Section 2.8 Problem Set #2 41
So we might replace this line in my answer for (1):
else when ( eq handler- 1 handler-2) collect handler- 1
into both-and-same
with:
else when (or (eq handler- 1 handler-2)
(equal ( f oo handler- 1) ( f oo handler-2)))
collect handler- 1 into both-and-same
where ( f oo handler) is:
(cdr (assq : f definition-location-hints
( si : cca-extra-inf o
( si : compiled-f unction-cca handler ) ) ) )
And if you used my partial answer for (2), the list returned by shexaminer-
compute-magic-list still contains everything you need to know, although it's
possible your answer to (2) threw away some of the information needed for
(3).
4. At the moment, this is an open problem — I don't know exactly what's
required to make such an addition. But it sure would be nice to have...
Chapter 3
MORE ON NAVIGATING THE LISP MACHINE
The last chapter discussed an aspect of programming with the lisp language, as
implemented on the lisp machine. This one is about some aspects of using the lisp
machine which are more or less independent of programming on it, i.e., what you
might call the operating system of the lisp machine.
3.1 The scheduler and processes
Switching back and forth among the different processes can be explicitly controlled
by the lisp machine programmer (read the documentation on Stack Groups), but
almost never is. A special module called the scheduler generally handles this
responsibility. Every l/60th second the scheduler wakes up and decides whether
the current process should be allowed to continue running, and if not, which other
process should get a chance.
If the current process has been running continuously for less than a second, and
wishes to continue, it is allowed to. (Note that a full second is a long time for this
sort of thing, compared to other timesharing arrangements.) Or if it's been running
for a second but no other process wishes to run, it is still allowed to continue. But
44 MORE ON NAVIGATING THE LISP MACHINE Chapter 3
if it's been monopolizing the machine for more than a second, and one or more
other processes want to run, it's forced to take a rest while the scheduler gives the
others a chance. The process chosen by the scheduler is now treated as the previ-
ous current process was: it will be allowed to run until some other process (es) wish
to run and the current process either volunteers to give the others a chance, or
passes the one second mark.
The way a process "volunteers to give the others a chance," or, in less emotionally-
laden terms, informs the scheduler that it doesn't need to run, is with the process-
wait function. Process-wait specifies a condition the process is waiting for. When
the condition becomes true, the process is ready to run. When the scheduler
decides to resume the process, the call to process-wait returns and the computation
continues from there. The first argument to process-wait is a string to appear in
the wholine (at the bottom of the screen) while the process is waiting. The second
arg is a function and any remaining args are arguments to the function. To see
whether the process is ready to continue, the scheduler applies the specified func-
tion to the specified arguments. The return value of the function is what the
scheduler uses for the "condition" mentioned above. This function is often called
the process' wait -function. Here is the process- wait which is responsible for "Tyi"
appearing in the wholine most of the time:
(PROCESS-WAIT "Tyi" SELF ':LISTEN)
This call is buried somewhere in the code windows (or anything with tv:stream-
mixin) use for reading from the keyboard. It says that the process will be ready to
continue when application of SELF to the argument : LISTEN returns non-nil.
Since funcall is equivalent to send when dealing with instances (see the previous
chapter), this process-wait will return when (send self : listen) is true.
The handler for .listen just checks to see if anything is in the io-buffer, so the pro-
cess which calls this process-wait will forfeit its turns in the scheduler until it has
something in its io-buffer.
Now a question for the bold: what happens if an error occurs in the scheduler? It
is, after all, just another piece of lisp code. And even if the scheduler code itself is
bug-free, all the wait-functions are called in the scheduler, and any loser* can
write a buggy wait-function. It's also the case that blinking of flashing blinkers
gets done from the scheduler. (There's a clock function list of things to be done
every time the scheduler runs, and by default the only thing on the list is blinking
the blinkers.) And any loser can also write a buggy :blink method for his/her
blinkers — I've certainly done it. So what happens when the scheduler runs into an
See hacker's definition at end of chapter.
Section 3.1 The scheduler and processes 45
error? The scheduler has no window to use. How can the debugger communicate
with you?
What happens is that the scheduler enters the debugger and uses what is called the
cold-load stream. This is a very basic stream which completely bypasses the win-
dow system. It uses the screen as it would a dumb terminal, with no regard for the
previous display contents, ignoring even window boundaries. There will be no
blinker (which makes typing somewhat disconcerting) and none of the input editor
commands will be active, apart from the rubout key. But you will be in a legiti-
mate debugger, from which you can attempt to set things right. So don't panic.
Our view of scheduling is now fairly complete. The current process owns the lisp
machine until it either does a process-wait, or uses up its second. When either of
these occurs, the scheduler calls the wait-functions of the other processes. The first
process whose wait-function returns a non-nil value gets to become the current-
process. If none of them do, the old current process remains the current process.
And if any errors occur while in the scheduler, the debugger uses the cold-load
stream.
Fine. Now it's time to complicate things again. At any given time a process is
either active or inactive. Inactive processes are not even considered by the scheduler
when it looks for an alternative to the current process. Their wait-functions aren't
called at all until they become active. And what makes a process active or inac-
tive? Two of the instance variables of a process are its run-reasons and its
arrest -reasons. An active process is one with no arrest reasons and at least one run
reason. Otherwise (at least one arrest reason or no run reasons) the process is inac-
tive. There are messages for looking at a process' run and arrest reasons, and for
adding to or deleting from them. A program might use those messages, but an
interactive user is more likely to arrest or un-arrest a process in one of the follow-
ing ways (all of which end up passing those same messages, but are easier to use) :
1. The system menu has options for arresting or un-arresting the process in the
window the mouse is over.
2. If you click on the name of a process in Peek's display of processes, you get a
menu of useful things to do to that process. Two of them are arresting and
un-arresting.
3. Typing Function- A arrests the process the wholine is watching. (This is usu-
ally the selected window's process. But you can change which process the
wholine watches with Function-W.) Function-minus-A un-arrests it.
Another common operation to perform on a process is to reset it. This is very
much like typing c-m-Abort to it. It flushes everything on the process' stack and
46 MORE ON NAVIGATING THE LISP MACHINE Chapter 3
restarts it. (More exactly, it reapplies the process' initial function to its initial
arguments, but you needn't understand that just yet.) You can only type c-m-
Abort to a process when you can select its window, which isn't always possible, but
you can reset a process anytime. The options for how to reset a process are similar
to those for un-/arresting one. You can send a process the :reset message; you can
use the reset option in the system menu to reset the process in the window under
the mouse; you can use the menu in Peek's display of processes.
Note that all these ways of resetting, except for explicitly sending the :reset mes-
sage, depend on being able to use the mouse. So if the mouse process is the one
which is in trouble, they won't work. The only way out is to get a handle on the
mouse process and send it the :reset message. An extremely useful fact to
remember is that the value of the symbol tv:mouse-process is always the mouse pro-
cess itself (an instance of flavor si:process). So typing this will often unwedge the
mouse process: (send tv: mouse-process :reset).
One final note on resetting: (send current-process : reset) doesn't work.
(It just returns nil.) The usual method for unwinding a stack doesn't work from
within that stack. To reset the current process, you need to either spawn a new
process for the sole purpose of resetting your process (use process-run-function) , or
use an optional argument to the reset message: (send current-process
: reset : always ) will work.
Before long you will probably have cause to create your own processes. Take a
look at Part III of Volume 8 of the Symbolics documentation when the need arises.
3.2 Windows
The entire set of existing windows is organized into several trees. The root of each
tree is a screen. (Screens are built on tv:sheet but don't have all the other mixins
that make a window.) Each window has a superior (towards the root of the tree)
and any number (possibly 0) of inferiors (towards the leaves) . The windows option
in Peek displays all the trees (subject to a restriction mentioned below).
The state of a window may be characterized in any of several ways. The window
may be selected or deselected, it may be exposed or deexposed, and it may be
activated or deactivated. The terminology is confusing and unfortunate. Selection,
exposure, and activation are not independent factors. In fact, they are closely tied.
Before a window may be selected, it must be exposed. And before it may be
exposed, it must be activated. So there are four possible states for a window:
deactivated; activated but not exposed (usually called deexposed); activated and
Section 3.2 Windows 47
exposed but not selected (usually called exposed); and activated, exposed, and
selected (usually called selected).
If a window is deactivated, the system will not keep track of it. More precisely, the
window will not appear in the array which is the value of tv:previously-selected-
windows. Many parts of the system software, including Peek, use that array when
they are expected to produce a list of all windows. And a deactivated window will
not appear in the inferiors list of its superior. The system will not keep any
pointers to such a window, so unless you have one, the window will be garbage-
collectible. (Ignore this point for now if you don't understand garbage collection.)
Once activated, a window may become exposed. Being exposed means roughly that
the window has somewhere for its typeout to go. Any window which is completely
visible on the screen, not even partly covered by some other window, is exposed. (It
is also possible for windows to be exposed without being visible. Such a window
must have somewhere for its typeout to go other than your screen — that place
would be a bit-array which could later be mapped onto the screen. See sections
11.4 and 11.5, Pixels and Bit-save Arrays, and Screen Arrays and Exposure, in
Volume 7. For now let's just assume that only visible windows are exposed.)
If a window which is not exposed is asked to type something out (perhaps with the
:tyo or :string-out messages), it won't be able to do it, since it has no place to send
the typeout. How it reacts is controlled by its deexposed -typeout -action, which is
an instance variable of tv:sheet. It may specify, for instance, that the window
should try to expose itself, or that an error should be signaled. The default value of
deexposed-typeout-action, : normal, specifies that the process doing the typeout
should enter an output hold state. That means it will do a process-wait (remember
those?) with a wholine state of "Output Hold" and a wait-function which essen-
tially waits for the window to become exposed:
(PROCESS-WAIT "Output Hold"
#' (LAMBDA (SHEET)
(NOT (SHEET-OUTPUT-HELD-P SHEET)))
SELF)
In addition to the usual ways of exposing the window (mentioned below), when an
output hold occurs there is one extra way which becomes available. That is to type
Function-Escape.
Now for selection. Although any number of windows may be simultaneously
exposed, as long as they can all fit on your screen without overlapping, only one
window at a time may be selected. The currently selected window is always the
48 MORE ON NAVIGATING THE LISP MACHINE Chapter 3
value of the symbol tv:selected-window. The selected window is the one to which
keyboard input is directed. It usually has a blinking rectangular cursor in it.
There must, of course, be a process running in the selected window for it to do any-
thing with the keyboard input. If the selected window has no process, typing on the
keyboard has no effect, except for special keys like Function and Select.
If we imagine the four possible window states (deactivated, deexposed, exposed,
selected) occupying a spectrum, the various messages for changing the state of a
window may be pictured as follows:
ACTIVATION EXPOSURE SELECTION
DEACTIVATED
DEEXPOSEO
EXPOSED
SELECTED
ACTIVATE
EXPOSE
SELECT
DEACTIVATE
DEEXPOSE
DESELECT
Figure 3. Transitions among window states
The meaning of the arrows is that a window sent the given message will be pushed
all the way from its current state to the head of the arrow. So, for instance, if an
exposed window is sent the :deactivate message, it will be both deexposed and deac-
tivated. A selected window would be deselected, deexposed, and deactivated. The
messages only push in the direction of the arrows, they don't pull. That is, if the
window is already at or beyond the arrowhead nothing happens. If a selected win-
dow is sent the :activate message, there is no effect. It is not pulled back to the
deexposed state.
A freshly instantiated window, as is returned by tv:make-window, will be deac-
tivated, unless you specify otherwise. This is also the state of a window which has
been sent the deactivate or :kill messages. (Killing a window deactivates all of its
inferiors as well as itself.)
You can always change the state of a window by sending it an appropriate mes-
sage, but there are several ways to make these messages be sent without explicitly
sending them yourself. The system menu has an option for killing the window
Section 3.2 Windows 49
under the mouse, and one for selecting a window from the list in tv:previously-
selected-windows. The Edit Screen option in the system menu pops up another
menu with options for killing or exposing any partially visible window, and for
exposing any window in tv:previously-selected-windows. (The Edit Screen menu
also has options for creating, moving, or reshaping windows.) And if you click on
the name of a window while in the windows display of Peek, you get a menu with
options for selecting, deselecting, exposing, deexposing, deactivating or killing the
window.
There's another way to select a window which you are already familiar with: use
the Select key. For the kinds of windows accessible via the Select key (Select-Help
displays a list), the effect of the Select key depends on how many instances of that
flavor of window exist. Let's take Select-E (for the Zmacs Editor) as an example.
If there are no existing Zmacs windows, typing Select-E will create one and select
it. If there is exactly one Zmacs window, Select-E will select it (unless it is already
the selected window, in which case the screen will flash and it will remain the
selected window) . If there are more than one existing Zmacs windows, and none of
them are the selected window, Select-E will select the one which had most recently
been the selected window. Typing Select-E repeatedly will rotate through all the
existing Zmacs windows.
Typing Select-c-E (hold down the control key while striking E) will always create
and select a new Zmacs window, regardless of whether there are already some.
Windows can also be selected with the Function key. Function-S selects the previ-
ously selected window. Providing a numeric argument between the Function key
and S allows rotation of the selected windows in various arcane ways. Type
Function-Help and read about S for a full description.
Changing the state of a window will often cause the state of other windows to
change. For instance, if I select one window, the window which had been selected
necessarily becomes deselected. And if I deselect a window, some other window
(the previously selected one) becomes selected. Similarly, exposing a window may
partially or entirely cover some other window which had been exposed; the latter
window is forced to become deexposed. And deexposing a window may uncover
some other window, thereby exposing it. (A subtler point arises here. Simply send-
ing the :deexpose message usually does not have the intended effect. Since no other
windows will be covering the one which has just been deexposed, it will immedi-
ately be automatically re-exposed. It will look like nothing happened. What you
probably meant to do was either expose some other window [which will automati-
cally deexpose the first window], or send the first window the :bury message, which
in addition to deexposing it, puts the window underneath all the other windows, so
50 MORE ON NA VIGA TING THE LISP MACHINE Chapter 3
that the window that ends up being auto-exposed is some other one.)
The interactions among windows can become terribly convoluted. There are several
kinds of locks intended to keep everything straight. If something goes wrong and
an error occurs while the window system is locked, the debugger won't be able to
expose a window to use. So it uses the cold-load stream, just as when an error
occurs inside the scheduler.
If you've been messing with the window system in unwise ways, it's possible to get
it locked up so that you can't do anything (I do it all the time). If the window
which appears to be selected isn't responding to typein, and c-m-Abort doesn't help,
and the mouse is dead, and you can't select some other window with the Select or
Function keys, it may be that you're hung up in a locked window system. Your
last resort in such a case (short of h-c-Function and a warm or cold boot) is to type
Function-control-Clear Input. This clears all the locks in the window system. It's
a sledgehammer, and can easily break some things, but it may revive your machine
without having to boot.
One last note about windows. When a window is sent more than one screenful of
typeout at a time, it may pause at the end of each screenful, type **MORE**, and
wait for you to press any key before continuing. This behavior is called more pro-
cessing. Whether more processing occurs (as opposed to continuous output) is con-
trolled by Function-M and Function-c-M. Type Function-Help for details. For
more processing to occur it must be turned on both globally and for the individual
window.
3.3 Debugging
The trace feature can be invaluable in finding out why your code isn't doing what
you expected. You can read all about it in chapter 4 of volume 4. The basic form
works like this: you turn on tracing for a function named foo by evaluating
(trace foo). From then on, every time foo is called, a line will be printed on
your screen announcing that foo has been entered and listing its arguments. And
when foo finishes another line will be printed, announcing the exit from foo and
listing the return value(s). You turn off tracing of foo with (untrace foo).
Some of the fancier features allow you to print the values of arbitrary expressions
upon function entry or exit, or to make tracing conditional on some predicate, or to
enter a break loop or the debugger upon entry. The syntax for these features can
be difficult to remember, so I'd suggest using the trace menu to select them. (You
get the trace menu by clicking on trace in the system menu or by doing Meta-X
Trace in the editor.)
Section 3.3 Debugging 51
Trace can be used on anything you can describe with a function spec (see chapter
27 of volume 3). Function specs are most often symbols, but can also be something
like (: method tv: sheet : expose), referring to the :expose method for flavor
tv:sheet.
One note of caution: you can instantaneously make your machine unusable by
tracing the wrong function. If, for instance, you'd traced some function that gets
called every time a character is read from the keyboard, with the feature that
throws you into the debugger upon function entry, you'd have a real tough time
typing "untrace." (On the other hand, the kinds of functions that can break every-
thing when traced are not ones the new user is likely to be interested in anyway, so
don't feel inhibited by this warning. Go ahead and play. You can always boot. I
just thought I should mention that I've screwed myself this way more than once.)
Advising is a more general facility similar to tracing. Here you supply an arbitrary
piece of code to be executed upon function entry or exit. Advice comes in three
varieties: before, after and around. The three kinds are very much like before
daemons, after daemons, and whoppers. Advising is explained in detail in chapter
4, volume 4.
The debugger itself is the main tool for discovering what went wrong. You should
remember a few things about the debugger from Chapter 1 : it is entered whenever
an error occurs, and may be entered manually with the function ( dbg ) or by typ-
ing m-Suspend. Once in the debugger, you can move up and down the stack with
c-P and c-N (for previous and next), and see the whole stack with c-B (for back-
trace). There are generally a series of restart handlers bound to the super keys,
and to Resume and Abort.
Now some debugger facilities that may be new. The arguments of the function in
the current frame are accessible via the function dbg:arg. It takes one argument,
specifying which of the current function's arguments you want (you may specify
either by name or by number). Suppose the current function has an argument
named "array," and I want to know what element #5 in the array is. I could type
(aref (dbg:arg 'array) 5). The best part about dbg:arg is that it can be
used with setf to change the argument. So if the first argument to the current
function were the string "now" and I wanted to change it to the string "then", I
would type ( setf (dbg:arg 0 ) "then" ). (Note that the numbering of argu-
ments begins at 0, not 1 .) The function dbg:loc is analogous to dbg:arg, but works
on local variables instead of arguments.
You can also use the debugger commands c-m-A and c-m-L to get the values of
arguments and local variables. Returning to the case where I wanted to see
52 MORE ON NAVIGATING THE LISP MACHINE Chapter 3
element #5 of an array which was an argument in the current frame, I could type
(assuming the array were the third argument) c-2 c-m-A, which would return the
array object and give me a fresh debugger prompt, then (aref * 5), using * to
access the previously returned value.
Once you've re-arranged the arguments to your liking, you may use c-m-R to rein-
voke the current frame with the new arguments. Another useful command is c-R,
for returning a specified value from the current frame (the value is prompted for
after you type c-R).
Another handy command is c-E. It finds the definition of the function in the
current frame and reads it into the editor. (Of course, it reads the whole file the
function is defined in. You will be positioned in the buffer at the place where the
function is defined.) Finally, c-M (for mail) will set you up for sending a bug
report. You'll be switched to a mail window, with both a complete stack backtrace
and information on what version of the system software is running on your machine
inserted as the initial contents of your message. Add whatever text is necessary to
describe the situation. Check the top line of the buffer to make sure the "To:" field
contains the name of an appropriate recipient, and edit it if necessary. Then just
hit the End key. (c-End if you have the Bell Labs/Murray Hill standard utilities
— we use End for another editing function.) Off goes your mail, you get returned
to the debugger, and you may continue as you please. The inclusion of the back-
trace makes life much easier for your system maintainer.
3.4 Who Does What
Here are a hodge-podge of lisp functions for finding out about functions and sym-
bols (see "Poking Around in the Lisp World," section 17.1 in volume 1, for some
more): who-calls takes one argument, usually a symbol. It searches all defined
functions and collects those which call the symbol as a function or use it as a vari-
able or constant. This takes a long time. You can limit the search to certain pack-
ages by using the optional arguments to who-calls. Apropos takes one argument, a
string, and finds all symbols whose print-names include the string as a substring.
This also takes a long time, and also can be limited to certain packages by using
the optional arguments. Disassemble takes one argument, the name of a compiled
function or the function object itself. It prints a human-readable version of the
compiled instructions. Grindef pretty-prints the definition of a non-compiled (inter-
preted) function.
See also the Zmacs Meta-X commands List Callers, Edit Callers, Function Apro-
pos, List Combined Methods, Edit Combined Methods, List Methods, and Edit
Section 3.4 Who Does What 53
Methods.
Section 17.1.1 of volume 1 also describes some variables whose values are automati-
cally maintained by the lisp command loop. *, for instance, is always bound to the
value returned by the last form you typed in, and can be extraordinarily helpful.
3.5 The Input Editor and Histories
The input editor is active in most contexts outside of the editor. Most notably, it is
active when you're typing to a lisp listener. c-Help lists all the input editor com-
mands. Most of them are similar to the Zmacs commands, so you can do all sorts
of editing of the input before it gets to the lisp reader. Two of the more helpful
features of the input editor are the histories it keeps, the input history and the kill
history. Every time you send a form off to be evaluated by a lisp listener, the form
is added to that lisp listener's input history. (Each lisp listener keeps its own input
history, even the editor's typeout window.) Pressing the Escape key will display the
input history of the window you are typing to. Every time you delete more than
one character of text with a single command (with, for example, m-D, m-Rubout,
Clear Input, c-W, c-K), the deleted text is added to the kill history. There is only
one kill history; it is shared by all the windows which use the input editor, and also
the Zmacs window (s). c-Escape displays the kill history.
In both the input editor and in Zmacs, c-Y yanks the most recent item off the kill
history and inserts it at the current cursor position. You can select an earlier ele-
ment from the history by giving c-Y a numeric argument. Typing m-Y immedi-
ately after a c-Y does a yank pop; it replaces the text which has just been yanked
with the previous element from the history. Repeatedly typing m-Y will rotate all
the way through the history. Giving m-Y a numeric argument will jump that many
items in the history.
Note that since all windows share the same kill history, it provides a simple way of
transferring text from the editor into a lisp listener: just push the text onto the kill
history while in the editor, perhaps with c-W or m-W or a mouse command. Then
switch to a lisp listener, type c-Y, and presto! There's your text.
In the input editor, c-m-Y yanks from the input history. (c-C also has this effect.
It is an older command which some people still prefer to use. I find c-m-Y easier
to remember.) m-Y again has the effect of rotating through the history. (m-C is
the corresponding older command — you used to need different commands for
rotating through the kill history and the input histories, but now m-Y does both.)
54 MORE ON NAVIGATING THE LISP MACHINE Chapter 3
In Zmacs, c-m-Y has the effect of yanking from what's called the command his-
tory, which is a history of all editing commands which use the mini-buffer.
Immediately after a c-m-Y, m-Y has the usual effect.
The ability to yank previous inputs into a lisp listener raises an interesting question:
how does the input editor know when you're finished editing and ready for the input
to be sent off to lisp? Normally, if you just type your input without any yanking,
the input editor knows you're done when you type some sort of delimiter at the end
of the input string, like a close paren, to complete a well-formed lisp expression.
But if you've yanked an already well-formed expression, how can you complete it?
The answer is that there is a special activation character. It is the End key. Press-
ing End while anywhere within a well-formed expression tells the input editor
you're done, and it sends your input off to lisp. So if you've yanked a previous
input with c-m-Y, you can type End immediately to re-evaluate the same expres-
sion, or you can edit it some and type End when finished, to evaluate the modified
expression.
There's one other input editor command of special interest. That's c-sh-A, which
displays the argument list of the function whose name you have typed. So if I type
"(with-open-f ile " to a lisp listener, and then type c-sh-A, the following will
appear on my screen:
WITH-OPEN-FILE (MACRO): ((STREAM-VARIABLE FILENAME . OPTIONS) &BODY BODY)
c-sh-A also works in Zmacs.
3.6 Fun and Games
More definitions from The Hacker's Dictionary (Guy L. Steele Jr., et at),
prompted by my spontaneous use of the term loser.
LOSE verb.
1. To fail. A program loses when it encounters an exceptional condition or fails
to work in the expected manner.
2. To be exceptionally unaesthetic.
3. Of people, to be obnoxious or unusually stupid (as opposed to ignorant). See
LOSER.
DESERVE TO LOSE verb. Said of someone who willfully does THE
WRONG THING, or uses a feature known to be MARGINAL. What is
Section 3.6 Fun and Games 55
meant is that one deserves the consequences of one's losing actions. "Boy,
anyone who tries to use UNIX deserves to lose!"
LOSE, LOSE interjection. A reply or comment on an undesirable situation.
Example: "I accidentally deleted all my files!" "Lose, lose."
LOSER noun. An unexpectedly bad situation, program, programmer, or
person. Someone who habitually loses (even winners can lose occasionally).
Someone who knows not and knows not that he knows not. Emphatic forms
are "real loser," "total loser," and "complete loser."
LOSS noun. Something (but not a person) that loses: a situation in which
something is losing.
WHAT A LOSS! interjection. A remark to the effect that a situation is bad.
Example: Suppose someone said, "Fred decided to write his program in
ADA instead of LISP." The reply "What a loss!" comments that the choice
was bad, or that it will result in an undesirable situation — but may also
implicitly recognize that Fred was forced to make that decision because of
outside influences. On the other hand, the reply "What a loser!" is a more
general remark about Fred himself, and implies that bad consequences will
be entirely his fault.
LOSSAGE (lawss'.j) noun. The stuff of which losses are made. This is a
collective noun. "What a loss!" and "What lossage!" are nearly synonymous
remarks.
WIN
1. verb. To succeed. A program wins if no unexpected conditions arise. Anto-
nym: LOSE.
2. noun. Success, or a specific instance thereof. A pleasing outcome. A
FEATURE. Emphatic forms: MOBY win, super-win, hyper-win. For some
reason "suitable win" is also common at MIT, usually in reference to a satis-
factory solution to a problem. Antonym: LOSS.
BIG WIN noun. The results of serendipity.
WIN BIG verb. To experience serendipity. "I went shopping and won big;
there was a two-for-one sale."
WINNER noun. An unexpectedly good situation, program, programmer, or
person. Albert Einstein was a winner. Antonym: LOSER.
REAL WINNER noun. This term is often used sarcastically, but is also
used as high praise.
56 MORE ON NAVIGATING THE LISP MACHINE Chapter 3
WINN AGE (win'.j) noun. The situation when a LOSS AGE is corrected or
when something is winning. Quite rare. Usage: also quite rare.
WINNITUDE (win':-tood) noun. The quality of winning (as opposed to
WINNAGE, which is the result of winning) .
Section 3.6 Fun and Games 57
3.7 Problem Set #3
Questions
Evaluate (make-system 'funny-window rnoconfirm : silent). This
will load some code, and create and select a window of flavor funny-window, mak-
ing it the value of the symbol * funny -window*.
The funny-window flavor is built on tvrlisp-listener, so you can type forms to this
window and it will evaluate them just as a lisp listener would.
This window has two behavioral quirks, one which occurs just before it types out
the return value of whatever it evaluates, and one which occurs whenever you move
the mouse.
The assignment is to find out what's responsible for the odd behavior. You win the
game if you can get the code causing the behavior into a Zmacs buffer. Use what-
ever you know about looking into the state of the lisp machine. Several good tech-
niques are buried in the text of this chapter.
There's one restriction on what you may do: you should read the code into the edi-
tor by using some system-provided operation which finds the definition of a given
function. So you have to first figure out what function is responsible. It's cheating
to randomly read in files and scan them.
I would suggest starting on the typeout quirk — it should be a little easier to track
down than the mouse behavior.
58 MORE ON NAVIGATING THE LISP MACHINE Chapter 3
Hints
Note that there's a delay partway through the funny typeout. To find out what's
going on, you can simply do c-m-Suspend during the delay and look at what's on
the stack. (At this point you might want to (setq *funny-typeout-
delay* 0 ) for the duration.) Use c-E on the appropriate frame to pull the
offender into the editor.
Finding the mouse funniness isn't as easy. c-m-Suspend doesn't work — and not
only because there's no time for it. Try setting *funny-mouse-delay* to 1 (sec).
Now there's time to type c-m-Suspend during the funniness, but the stack shows
nothing revealing, because the action is in a different process. (When convinced,
you should probably set *funny-mouse-delay* back to 0.)
One possible way to proceed would be to put a trace (with :error) on the : draw-
circle method, which you can see is being called. [To get the method do
(get-handler-for * funny- window* : draw-circle), which you will see
turns out to be (: method tv:graphics-mixin : draw-circle) J. That
would force the process which is calling : draw-circle into the debugger.
Unfortunately everything gets fouled up when the mouse process, which turns out
to be the culprit, goes into the debugger. (The trouble starts because the mouse
process doesn't have a window to use, so it has to come up with one and expose it.
That in itself is okay [processes which run in the background generally have that
capability], but the real killer is that the mouse process has to be running in order
for most ways of switching windows to work. With the mouse process halted for
debugging, most window system operations are impossible, and the lisp machine is
suddenly in a terribly wedged* state! (It usually isn't necessary to re-boot if this
happens — try resetting the mouse process, with (send tv: mouse-process
WEDGED adjective.
1 . To be stuck, incapable of proceeding without help. This is different from having CRASHED.
If the system has crashed, then it has become totally nonfunctioning. If the system is
"wedged," it is trying to do something but cannot make progress. It may be capable of doing
a few things, but not be fully operational. For example, the system may become wedged if the
disk controller FRIES; there are some things you can do without using the disks, but not many.
Being wedged is slightly milder then being "hung." This term is sometimes used as a synonym
for DEADLOCKED. See also HANG, LOSING, CATATONIA, and BUZZ.
2. Of a person, suffering severely from misconceptions. Examples: "He's totally wedged — he's
convinced that he can levitate through meditation." "I'm sorry. I had a BIT set that you were
responsible for TECO, but I was wedged."
(The Hacker's Dictionary, Guy L. Steele, Jr., et at)
Section 3.7 Problem Set #3 59
: reset). If you can't get to a window that will accept typein, do Function-
Suspend first.)
If you really want to make this approach work, here's something a little more
advanced (that's understated — it took two of us quite a while to come upon it)
that will produce the desired effect:
(advise (: method tv:graphics-mixin : draw-circle) : before nil nil
(send (send * funny-window* : process)
: interrupt #'dbg current-process)
(process-sleep 60.))
The basic idea is to force the process calling : draw-circle into the debugger by
calling the dbg function on it, but to do the call to the dbg function from the funny
window, using its process and its already exposed and selected window. Now you
can examine the stack and quickly find the : before : mouse-moves method
which is responsible.
But there's a much more sensible approach. You could think of the above force-it-
into-the-debugger approach as finding the set of functions which are being called
and filtering that set for functions which seem related to the funny-window flavor.
Turning that around is far more effective in this case. That is, first collect the
functions related to the funny-window flavor, which is much easier in this case than
getting access to the control stack, and then filter that set for ones likely to be caus-
ing the funny effects. So one idea is to do a :which-operations on the win-
dow and search for messages with suggestive names. You could, for instance, try
(loop for msg in (send * funny- window* rwhich-operations)
when (string-search "mouse" msg) collect msg)
[If you have the Murray Hill standard utilities, you could simply do (grep-msgs
"mouse" * funny- window*).! This will be a small enough set that they could
all be investigated. A more direct approach, and the clear winner for this problem,
is based on the observation that although the funny-window flavor has many
methods defined, the bulk of them are inherited from other flavors, and whatever is
responsible for its peculiar behavior must be somewhere in the relatively small
number of methods defined locally for funny- window. So all you have to do is enter
the Flavor Examiner (Select-X), and get the list of local methods for funny-
window. How about that. Two methods, one for each peculiarity. Now you can
edit them either by clicking mouse-right while pointing to one, and choosing "edit"
from the menu, or by clicking where it says "edit" on the extreme right.
60 MORE ON NAVIGATING THE LISP MACHINE Chapter 3
Solutions
Here's what causes the typeout funniness:
(defvar *funny-typeout-delay* 1)
; ; ; You found the funny typeout stuff !
(def whopper (funny-window : input-editor ) (krest args )
(let ((thing ( lexpr-continue-whopper args)))
(format self "-&You entered something of type ")
(process-sleep (* 60 *funny-typeout-delay* ) )
(princ (typep thing) self)
thing))
And this causes the mouse funniness:
(defvar *funny-mouse-delay* 0)
; ; ; You found the funny mouse stuff I
(def method (funny-window : before : mouse-moves ) (x y)
(format self "-&The mouse is moving . . . ")
(process-sleep (* 60 * funny-mouse -de lay* ) )
(send self : draw-circle
(- x tv: left-margin-size) (- y tv: top-margin-size ) 8
(send self :tyo #\ ! ) )
Chapter 4
FLOW OF CONTROL
In this chapter we leave behind the "operating system" of the lisp machine and
return to aspects of the lisp language itself. In particular, we'll look at the various
constructs for determining the flow of control. The notes are a little sketchier than
usual, because this material is covered reasonably well in Part V of volume 2 of the
Symbolics documentation.
4.1 Conditionals
The cond special form is the basic conditional. The arguments to cond are any
number of clauses, where each clause is a list containing a predicate (the
antecedent) and zero or more consequents. The clauses are handled one at a time,
in order of appearance. If a clause's antecedent returns nil, its consequents are
skipped and the next clause is considered. When a clause is found whose
antecedent returns a non-nil value, that clause's consequents, if any, are all
evaluated. The value of the last consequent in that clause (or of the antecedent, if
there are no consequents) is the value returned by the cond, and the rest of the
clauses are skipped. If every clause's antecedent returns nil, the cond returns nil.
62 FLOW OF CONTROL Chapter 4
Beyond their value as logical operators, and and or can also be used as conditionals.
If one of the subforms of an and returns nil, the rest will be skipped. And if one of
the subforms of an or returns non-nil, the rest will be skipped. Here are two exam-
ples taken from the manual:
(and bright-day
glorious-day
(print "It is a bright and glorious day."))
(or it-is-f ish
it-is-fowl
(print "It is neither fish nor fowl."))
Almost all of the remaining conditionals are really macros which expand into calls
to cond. when and unless take a predicate and any number of consequent forms. If
the predicate for a when returns non-nil, all of the consequent forms are evaluated
and the value returned by the last one is returned by the when. Otherwise (a nil
predicate value) the consequent forms are all skipped, unless works similarly: if
the predicate returns nil the consequents are evaluated, otherwise they're skipped.
Here are the definitions of the when and unless macros:
(DEFMACRO WHEN (TEST &BODY BODY)
N ( COND ( , TEST ( PROGN , @>BODY ) ) ) )
(DEFMACRO UNLESS (TEST &BODY BODY)
"(COND ((NOT ,TEST) (PROGN ,@BODY) ) ) )
[Actually, I lied a bit in the previous paragraph. Although they could, and in fact
used to, have the given macro definitions, when and unless are now special forms
instead. I used the old macro definitions because they're easier to read, while for
our immediate purposes, any differences in behavior aren't important.]
Defining suitable macros is an extremely common (and effective) method for
extending the syntax of lisp. There are no extra levels of function calling at run-
time, and no modifications to the compiler are involved.
if is somewhat similar to when, and also macroexpands to a call to cond. If the test
evaluates to non-nil, the first body form is evaluated. Otherwise (test returns nil),
if there are any body forms following the first, they are all evaluated.
Three more macros based on cond are select, selector, and selectq. Selectq is used
most frequently. Here is its structure:
Section 4.1 Conditionals 63
( selectq key-form
(test consequent consequent . . . )
( test consequent consequent . . . )
( test consequent consequent . . . )
...)
The key-form is evaluated and checked against the tests. The tests are not
evaluated. Each test must be either a symbol, a fixnum, or a list of symbols and
fixnums. If the test (or one of its elements, if it's a list) is eq to the evaluated key-
form, then it matches. The symbols t and otherwise are special: a test consisting
of one of those two symbols always matches. As with cond, the consequents to the
first test which matches are evaluated, and the rest of the clauses are skipped.
Select is the same as selectq, except that the symbols in the tests are evaluated
before being compared to the (evaluated) key-form. Selector is like select except
that there is an additional argument (following the key-form) which is the name of
a function to use for the comparison in place of eq.
See also typecase, dispatch, cond-every, and selectq-every.
4.2 Blocks and Exits
block is the primitive for defining a piece of code which may be exited from the
middle. The first argument to block must be a symbol. It is not evaluated, and
becomes the name of the block. The rest of the arguments are forms to be
evaluated. If a call to return-from occurs within the block, with a first argument of
the block's name, the block is immediately exited. The rest of the arguments to
return-from are evaluated and become the return values for the block. (Beginning
with release 6.0, the preferred way of returning multiple values is with (return-
from name (values form... ) ), for compatibility with Common Lisp.)
The scope of the block is lexical, so the corresponding call to return-from must
occur textually within the block; it will not work to call return-from inside a func-
tion which is called within the block. The next section discusses that sort of nonlo-
cal exit.
Blocks may be nested; that's the whole point of naming them. A return-from
causes an immediate exit from the innermost block with a matching name.
Some other constructs (including do and prog) create implicit blocks. These blocks
64 FLOW OF CONTROL Chapter 4
have nil for a name, and so they may be prematurely exited with (return-
from nil (values value...) ). They may also be exited with the return special
form, which always exits the innermost block named nil.
4.3 Nonlocal Exits
catch and throw are analogous to block and return-from, but are scoped dynami-
cally rather than lexically. This means that a throw may cause an exit from any
catch on the control stack at the time the throw is reached (unless an inner catch is
shadowing an outer catch with the same tag) . catch's equivalent to the name of a
block is its tag. The tag is the first argument to catch; it is evaluated, and may
return any lisp object. The first argument to throw is its tag. It is also evaluated,
and the throw causes the exit of the innermost catch whose evaluated tag is eq to
the throw's evaluated tag.
If a throw occurs, its second argument is evaluated and the value (s) returned will
be returned by the corresponding catch. If no throw occurs, the values returned by
the catch are the values returned by its last subform.
There are obsolete forms of catch and throw, called *catch and *throw. They differ
from the newer version mainly in what values are returned. *catch and *throw
should not be used in new code.
4.4 Iteration
There are three styles of built-in facilities for iteration. A group of operators are
available for mapping a function through one or more lists; the do special form
allows more general forms of iteration; and the loop macro provides even more
flexibility. (I should also point out that functions which make use of tail recursion
are compiled into iterative structures.) This set, of course, could easily be extended
by writing more macros.
When more than one of the iteration facilities is applicable to a particular task, the
choice is mainly a matter of personal taste.* All three are comparably efficient.
The key issue is readability, and on this score opinions differ. My own view is that
the mapping functions are succinct and to the point, and therefore desirable, within
the limited set of applications that are easily expressed as mapping operations. For
all other kinds of iteration, do is often concise but I find it somewhat obscure.1 I
See hacker's definition at end of chapter.
Section 4.4 Iteration 65
think loop is much easier to read, but there are those who consider it wordy and
nebulous.
"Loop forms are intended to look like stylized English rather than
Lisp code. There is a notably low density of parentheses, and
many of the keywords are accepted in several synonymous forms to
allow writing of more euphonious and grammatical English. Some
find this notation verbose and distasteful, while others find it flexi-
ble and convenient. The former are invited to stick to do."
[The preceding, as well as parts of the discussion of loop below, is
taken from the loop documentation written by Glenn S. Burke:
MIT Laboratory for Computer Science TM-169.]
For the sake of comparison, here are four ways to print the elements of a list:
(defun print-eltsl (list) ; mapc
(mapc #'print list))
(defun print-elts2 (list) ; do
(do ( (1 list (cdr 1) ) )
((null 1))
(print (car 1) ) ) )
(defun print-elts3 (list) ; loop
(loop for elt in list
do (print elt) ) )
(defun print-elts4 (list) ; tail recursion
(unless (null list)
(print (car list))
(print-elts4 (cdr list))))
4.4.1 Mapping
The mapping operators come in six varieties. They all take as arguments a func-
tion followed by any number of lists, and step through all the lists in parallel,
applying the function as they go. They vary along two dimensions: whether they
operate on successive elements of the lists or on successive sublists, and which of
t ditto.
66 FLOW OF CONTROL Chapter 4
three kinds of values they return. (Two x three = six.) The return value can be
simply the second argument to the mapping operator (implying that the mapping
was done for side effect only), or a list of the results of function application, or a
list formed by nconcing (destructive appending) the results of function application.
Here are a few examples:
Applied to successive elements, lists results
(mapcar #' + '(1 2 3 4) '(2 4 6 8))
-(369 12)
(mapcar #'list '(1 2 3 4) '(2 4 6 8))
- ((1 2) (2 4) (3 6) (4 8))
Successive elements, nconcs results
(mapcan #'list '(1 2 3 4) '(2 4 6 8))
- (1224364 8)
Successive sublists, lists results
(maplist #'( lambda (1) (and (second 1)
(+ (first 1) (second 1))))
'(1 2 3 4))
-(357 NIL)
(maplist #'( lambda (1) (list (first 1) (second 1)))
'(1 2 3 4))
- ((1 2) (2 3) (3 4) (4 NIL))
Successive sublists, nconcs results
(mapcon #'( lambda (1) (list (first 1) (second 1)))
'(1 2 3 4))
-(1223344 NIL)
4.4.2 Do
A do looks like this:
(do ( ( var init repeat )
Section 4.4 Iteration 67
( var init repeat ) . . . )
{end-test exit-form exit-form . . . )
body -form body -form . . . )
The first subform is a list of index variable specifiers. Upon entering the do, each
var is bound to the corresponding init. And before each subsequent iteration, var is
set to repeat. The variables are all changed in parallel. The end-test is evaluated
at the beginning of each iteration. If it returns a non-nil value, the exit -forms are
all evaluated, and the value of the last one is returned as the value of the do. Oth-
erwise the body -forms are all evaluated. Here's an example which fills an array
with zeroes:
(do ((i 0 (1+ i))
(n (array- length foo-array) ) )
((= i n))
(setf (aref foo-array i) 0))
Upon entry, i is bound to 0 and n is bound to the size of the anay. On each itera-
tion, i is incremented, (n stays constant because it has no repeat form.) When i
reaches n, the do is exited. On each iteration, the ith element of foo-array is set to
0.
And another, which is equivalent to (maplist #'f x y):
(do ((x x (cdr x))
(y y (cdr y) )
(z nil (cons (f x y) z)))
((or (null x) (null y) )
(nreverse z) ) )
Note that the preceding example has no body. It's actually fairly common for all
the action in a do to be in the variable stepping.
There are macros named dotimes and delist which expand into common do con-
structs. For instance, the following macroexpands into the first example above:
(dotimes (i (array-length foo-array))
(setf (aref foo-array i) 0))
68 FLOW OF CONTROL Chapter 4
4.4.3 Loop
loop is a macro which expands into a prog with explicit go statements, and often
with several lambda bindings. A typical call looks like this:
( loop clause
clause
clause . . . )
Each clause begins with a keyword, and the contents of the rest of the clause
depend on which keyword it is. Some clauses specify variable bindings and how the
variables should be stepped on each iteration. Some specify actions to be taken on
each iteration. Some specify exit conditions. Some control the accumulation of
return values. Some are conditionals which affect other clauses. And so on. A full
discussion of all the clauses would be lengthy and not particularly useful, as they're
all described coherently enough in the documentation. We'll just look at some
representative examples.
The repeat clause specifies how many times the iteration should occur. The key-
word is followed by a single lisp expression, which should evaluate to a fixnum.
And the do keyword is followed by any number of lisp expressions, all of which are
evaluated on each iteration. So putting the two together,
( loop repeat 5
do (print "hi there"))
prints "hi there" five times. The most commonly used (and complicated) of the
iteration-driving clauses is the for clause. The keyword is followed by the name of
a variable which is to be stepped on each iteration, then some other stuff which
somehow specifies the initial and subsequent values of the variable. Here are some
examples:
( loop for elt in expr expr is evaluated (it better return a list), elt is bound in
do (print elt) ) turn to each element of the list, and then the loop is exited
( loop for elt on expr similar, but elt is bound to each sublist
do (print elt) )
. . . for x = expr . . . expr is re-evaluated on each iteration and x
is bound to the result (no exit specified here)
. . . for x = exprl then expr2 ... x is bound to exprl on the first iteration
Section 4.4 Iteration 69
and expr2 on all succeeding iterations
. for x from expr ... x is bound to expr (it better return a fixnum) on the first
iteration and incremented on each succeeding iteration
. for x from exprl to expr2 . . . like above, but the loop is exited
after x reaches expr2
. for x from exprl below expr 2 . . . like above, but the loop is exited
just before x reaches expr2
. for x from exprl to expr2 by expr3 . . . incremented by exprS
on each iteration
. for x being path . . . user definable iteration paths
When there are multiple for clauses, the variable assignments occur sequentially by
default (parallel assignment may be specified), so one for clause may make use of
variables bound in previous ones:
(loop for i below 10 i starts at 0 when from isn't specified
for j = ( * i i ) . . . )
The with clause allows you to establish temporary local variable bindings, much like
the let special form. It's used like this:
( loop with f oo = expr expr is evaluated only once, upon entering the loop
... )
A number of clauses have the effect of accumulating some sort of return value.
The form
(loop for item in some-list
collect ( f oo item))
applies foo to each element of some-list, and returns a list of all the results, just like
(mapcar #'foo some-list). The keywords nconc and append are similar, but
the results are nconc-ed or appended together. Keywords for accumulating numeri-
cal results are count, sum, maximize, and minimize. All of these clauses may option-
ally specify a variable into which the values should be accumulated, so that it may
be referenced. For instance,
70 FLOW OF CONTROL Chapter 4
(loop for x in list-of-frobs
count t into count-var "t" means always count
sum x into sum-var
finally (return (// sum-var count-var)))
computes the average of the entries in the list.
The while and until clauses specify explicit end-tests for terminating the loop
(beyond those which may be implicit in for clauses). Either is followed by an arbi-
trary expression which is evaluated on each iteration. The loop is exited immedi-
ately if the expression returns the appropriate value (nil for while, non-nil for until).
(loop for char = (send standard- input :tyi)
until (char-equal char #\end)
do (process-char char))
The when and unless clauses conditionalize the execution of the following clause,
which will often be a do clause or one of the value accumulating clauses. Multiple
clauses may be conditionalized together with the and keyword, and if -then-else
constructs may be created with the else keyword.
( loop for i from a to b
when (oddp i)
collect i into odd-numbers and do (print i)
else collect i into even-numbers)
The return clause causes immediate termination of the loop, with a return value as
specified:
(loop for char = (send standard- input :tyi)
when (char-equal char #\end)
return "end of input"
do (process-char char))
Please refer to the documentation for a more complete discussion of loop features.
Of particular importance are prologue and epilogue code (the initially and finally
keywords), the distinction between ways of terminating the loop which execute the
epilogue code and those which skip it, aggregated boolean tests (the always, never,
and thereis keywords), the destructuring facility, and iteration paths (user-definable
iteration-driving clauses) .
Section 4.4 Iteration 71
4.5 A Bit More on Working with Macros
Since so many of the control structure constructs are macros, you'll probably find it
helpful in working with them to be able to see what forms they macroexpand into.
The most basic of the tools for expanding macros is the macroexpand function. It
takes a list as an argument and returns its expanded version:
(macroexpand '(if (test) (do-this) (do-that))))
-» (COND ((TEST) (DO-THIS)) (T (DO-THAT)))
The grind-top-level function, for pretty-printing, is also useful in conjunction with
macroexpand.
Rather than type (grind-top-level (macroexpand ... ) ) repeatedly, an
easier way to expand forms in a lisp listener is with mexp. Evaluating (mexp)
puts you into a macro reading and expanding loop. You type a form, and it
pretty-prints the expansion and waits for another input form. Exit by pressing the
End key.
But by far the easiest way to see what something expands into is the c-sh-M com-
mand in the editor. When you place the cursor at the beginning of a lisp expres-
sion and type c-sh-M, the macro-expansion of the form is shown in the typeout win-
dow. If you give c-sh-M a numeric argument, it inserts the expanded form into the
buffer. c-sh-M only expands the top-level form, not any of its subforms which are
themselves macros. m-sh-M expands subforms as well.
Of course, you can also use Meta-. on a macro to edit its definition, but it's often
more useful to see what a macro expands into than to see how it does it.
4.6 Fun and Games
From The Hacker's Dictionary, Guy L. Steele, Jr., et at
TASTE noun.
Aesthetic pleasance; the quality in programs which tends to be inversely pro-
portional to the number of FEATURES, HACKS, CROCKS, and KLUGES
programmed into it.
OBSCURE adjective.
Little-known; incomprehensible; undocumented. This word is used, in an
exaggeration of its normal meaning, to imply a total lack of
72 FLOW OF CONTROL Chapter 4
comprehensibility. "The reason for that last CRASH is obscure." "That
program has a very obscure command syntax." "This KLUDGE works by
taking advantage of an obscure FEATURE in TECO." The phrase
"moderately obscure" implies that it could be figured out but probably isn't
worth the trouble.
Fun and Games 73
4.7 Problem Set #4
Questions
1. A. Write a function called do-word which takes one required argument, a
symbol, and two optional arguments, both fixnums. If the symbol is
one of : point, : rectangle, : triangle, or : circle, the func-
tion should draw the specified kind of object on standard-output, at the
coordinates specified by the fixnum arguments (choose any size you like
for the figures). If the symbol is : reverse, the function should tog-
gle the state of reverse-video-p for standard-output. If the sym-
bol is : erase, the function should clear standard-output. If the sym-
bol is anything else, it should call the function beep.
B. Write another function, called read-chars, which reads a series of char-
acters from the keyboard. When the Return key is pressed, it makes a
string out of all the characters read (before Return), and returns a
symbol in the keyword package whose print-name is that string. That
is, if the characters "s," "a," "m," "p," "1," "e" and "<cr>" were
typed, the symbol : sample would be generated. (Normally, you
wouldn't write such a function because there are already several built-
in functions which do the same sort of thing and with more features.
But for the sake of demonstrating a point or two...)
C. Write a third function, called main-loop, which repeatedly calls read-
chars and then do-word on the result. It should provide the optional
arguments to do-word, and increment one or both each time so that the
figures are drawn across the top of the screen, and then in a second
row below the first, and so on, with each position 200 pixels from the
previous one. That is, if standard-output is a window whose dimensions
are 1088 by 749, the optional arguments should be (0,0), then (200,0),
(400,0), (600,0), (800,0), (0,200), (200,200) ... (400,800). (Don't
worry about resetting the coordinates after an : erase, or not step-
ping after a : reverse — just allow blank spots.) This version of
main-loop should iterate until it reaches the bottom right corner of the
window. You'll probably want to touch up do-word so that it draws
the figures centered in the 200 x 200 area whose upper left corner it is
given.
D. Alter main-loop and (read-chars) so that if at any point you press the
End key, even in the middle of typing a word, main-loop will return
immediately.
E. Make similar alterations so that the Line key will have the effect of
74 FLOW OF CONTROL Chapter 4
skipping any remaining positions in the current row and continuing at
the beginning of the next row.
2. Write three functions which meet the following spec, one using a mapping
operator, one using do, and one using loop: f takes one argument, a list of
fixnums, and returns a list containing only the even numbers from its argu-
ment. That is,
(evens '(12345)) -» (2 4)
3. Like (2), but this time the list which f returns includes only those elements of
the argument which are less than the succeeding element. That is,
(less-thans '(36725435)) - (3623)
4. Write a function which takes two arguments, a fixnum "n" and a list "1,"
and uses a mapping operator to return a list consisting of the elements of 1
incremented by n. Try to do this two ways, one using a lambda expression as
the function argument to the mapping operator, and one just using # ' +.
5. Write a function which takes one argument, a fixnum n, and returns the nth
fibonacci number, subject to the restriction that the time it takes should
increase linearly with n. I suggest using loop.
6. Write a function of no arguments which finds all prime numbers between 2
and 50000. (You might find it easier to have the function stop at the first
prime greater than 50000.) I suggest using loop again.
7. The function mexp only expands the initial operator in an expression. If one
of the arguments to that operator itself uses a macro, that doesn't get
expanded. That is,
(if test (do-this)
(if another-test (do-that)
(do-the-other) ) )
expands into
(COND (TEST
(DO-THIS) )
(T (IF ANOTHER-TEST (DO-THAT) (DO-THE-OTHER))))
instead of
(COND (TEST
(DO-THIS) )
(T (COND (ANOTHER-TEST
Section 4.7 Problem Set #4 75
(DO-THAT) )
(T (DO-THE-OTHER) ) ) ) )
Write mexp-all, which behaves just like mexp except that it macroexpands
subexpressions and thus would produce the second of the expansions shown
above.
76 FLOW OF CONTROL Chapter 4
Hints
A. The body of do-word should consist of a selectq (or a defselect, which
is a macro that expands into a selectq). The messages you'll need to
send standard-output are : draw-point, : draw-rectangle,
: draw-triangle, : draw-circle, :reverse-video-p,
:set-reverse-video-p, and : clear-window.
B. To read each character, send :tyi to standard-input or terminal-io
(usually the same thing). Collect the characters into a list, and use
string-append to combine the characters into a string, and intern to
make the string into a symbol. Make sure the characters are converted
to upper case, either before or after they're collected into the string.
C. The : inside-size message to a window returns two values, the
width and height of the interior, in pixels. (The difference between
inside-size and size is the margin area, which the usual output mes-
sages can't draw in.) Use these (actually, 200 less than these) as the
upper bounds on your iteration. You probably want two nested loops,
an outer one for the y coordinate and an inner one for the x coordinate.
D. Wrap the body of main-loop in a catch, and have read-chars do a throw
at the appropriate point.
E. Same technique. This time the catch goes around the inner loop.
The mapping operator should be mapcan. The do would need a structure
very much like that produced by the dolist macro (which you might even
want to use), combined with some parts of the do example in the notes which
was equivalent to maplist. The loop would use collect, conditionalized
with when.
This time the mapping operator should be mapcon. The do and loop can be
done in any of several ways. There could be one iteration variable which
took on the value of successive sublists, and then you could take its first and
second elements and do the comparison. But better (because it requires
fewer redundant cdr's and exits more cleanly) would be to have the value of
the "first" variable be directly set to the previous value of the "second" vari-
able.
The lambda expression adds n to its argument. A very nice way to do this
without a lambda expression is to use a circular list. (You can make one
yourself with rplacd, but the circular-list function is more convenient.)
Lots of choices here. You probably want to use a repeat clause to control
the loop termination. But the tricky part is that you need to have a value
Section 4.7 Problem Set #4 77
from one iteration hang around through the next one. So you might want
something like the loop answer to (3), where one variable always takes on the
previous value of another one. And you'll probably want to use a finally
clause with an explicit call to return.
You probably want a subroutine which tests whether a number "n" is factor-
able by any of a list "1" of potential factors. The loop keyword thereis
may be helpful in writing this part. Then you'll want to use this in another
loop which tests each number from 2 up for factorability. When it finds a
prime, it should add it to the list of potential factors.
The editor commands Macro Expand Expression (c-sh-M) and Macro
Expand Expression All (m-sh-M) display exactly the same difference in
behavior we wish to see between mexp and mexp-all. So you should be able
to pull some code out of Macro Expand Expression All, and use it as a black
box to help you create mexp-all.
78 FLOW OF CONTROL Chapter 4
Solutions
A. (defun do-word (message ^optional x y)
(selectq message
(: point (send standard-output : draw-point x y) )
(: rectangle (send standard-output : draw-rectangle
100 100 x y) )
(: triangle (send standard-output : draw-triangle
x y (+ x 100) y (+ x 50) (- y 87)))
(: circle (send standard-output : draw-circle x y 50))
(: reverse (send standard-output : set-reverse-video-p
(not (send standard-output
:reverse-video-p) ) ) )
(: erase (send standard-output : clear-window) )
(otherwise (beep))))
B. (defun read-chars ()
(loop for char = (send terminal-io :tyi)
until (char-equal char Areturn)
collect (char-upcase char) into char-list
finally (return (intern (apply #' string-append
char-list)
"keyword" ) ) ) )
C. (defun main-loop ( )
(multiple-value-bind (width height)
(send standard-output : inside-size )
(loop for y below (- height 200) by 200
do (loop for x below (- width 200) by 200
do (do-word (read-chars) x y) ) ) ) )
and in do-word
(: point (send standard-output : draw-point
(+ x 100) (+ y 100)))
(: rectangle (send standard-output : draw-rectangle
100 100 (+ x 50) (+ y 50)))
(: triangle (send standard-output : draw- triangle
(+ x 100) (+ y 57) (+ x 50) (+ y 143)
(+ x 150) (+ y 143)))
(: circle (send standard-output : draw-circle
(+ x 100) (+ y 100) 50))
Section 4.7 Problem Set #4 79
D. (defun main-loop ( )
(catch 'end-key
(multiple-value-bind (width height)
(send standard-output : inside-size )
(loop for y below (- height 200) by 200
do (loop for x below (- width 200) by 200
do (do-word (read-chars) x y) ) ) ) ) )
(defun read-chars ( )
(loop for char = (send terminal -io :tyi)
until (char-equal char Areturn)
when (char-equal char #\end)
do (throw 'end-key nil)
collect (char-upcase char) into char-list
finally (return (intern (apply #' string-append
char-list)
"keyword" ) ) ) )
E. (defun main- loop ( )
(catch 'end-key
(multiple-value-bind (width height)
(send standard-output : inside-size )
(loop for y below (- height 200) by 200
do (catch 'line-key
(loop for x below (- width 200) by 200
do (do-word (read-chars)
x y)))))))
(defun read-chars ( )
(loop for char = (send terminal -io :tyi)
until (char-equal char Areturn)
when (char-equal char #\end)
do (throw 'end-key nil)
when (char-equal char #\line)
do (throw 'line-key nil)
collect (char-upcase char) into char-list
finally (return (intern (apply #' string-append
char-list)
"keyword"))))
(defun evens (list)
(mapcan #' (lambda (n) (and (evenp n) (list n))) list))
80 FLOW OF CONTROL Chapter 4
(defun evens (list)
(do ((sublist list (cdr sublist))
( item)
(evens -found) )
( (null sublist)
(nreverse evens-found) )
( setq item (car sublist))
(if (evenp item) (push item evens-found))))
(defun evens (list)
(let (evens-found)
(dolist (item list)
(if (evenp item) (push item evens-found)))
(nreverse evens - f ound) ) )
(defun evens (list)
(loop for item in list
when (evenp item) collect item))
3. (defun less-thans (list)
(mapcon #' (lambda (1)
( and ( cdr 1 ) to avoid doing (< n nil) at the end
(< (first 1) (second 1) )
(list (first 1))) )
list))
(defun less-thans (list)
(do ((sublist list (cdr sublist))
(item) (good-ones))
( (= 1 (length sublist) )
(nreverse good-ones))
(setq item (car sublist))
(if (< item (second sublist))
(push item good-ones))))
(defun less-thans (list)
(loop for a = (car list) then b
for b in (cdr list)
when (< a b) collect a))
4. (defun add-to-list (n list)
(mapcar #' (lambda (elt) (+ elt n) ) list))
Section 4.7 Problem Set #4 81
(defun add-to-list (n list)
(mapcar #' + (circular-list n) list))
5. I've fudged the repeat counts of these three so that they'll agree on which is
the nth fibonacci number.
(defun linear-fib (n)
( loop repeat n
for a = 0 then b
for b = 1 then partial-sum
for partial-sum = (+ a b)
finally (return partial-sum)))
(defun linear-fib (n)
( loop repeat ( + n 2 )
for a = 0 then b
for b = 1 then partial-sum
sum a into partial-sum
finally (return partial-sum)))
(defun linear-fib (n)
(loop repeat (1+ n)
with a = 1
sum (progl a ( setq a partial-sum))
into partial-sum
finally (return partial-sum)))
6. This takes about 10 seconds to find all primes up to 50021 ("5100 of them).
(defsubst factorable? (n factors)
( loop for f in factors
while (< (* f f) n)
thereis (zerop (remainder n f))))
(defun primes ( )
(loop for n from 2
unless (factorable? n found)
collect n into found
and when (> n 50000) return (length found))))
7. The editor command Meta-X Macro Expand Expression All calls a function
named macro-expand-all to do the actual expansion. It's kind of messy, but
it basically does a recursive tree walk of the input form, expanding each part.
Understanding how it works, however, is not really necessary. All we need to
82 FLOW OF CONTROL Chapter 4
know is what it does; we can treat it as a black box. Here is the original
definition of mexp, and one for mexp-all, which modifies mexp to use macro-
expand -all.
( DEFUN MEXP ( )
(LOOP WITH (FORM FLAG)
DOING (FORMAT T "~2L")
(MULTIPLE -VALUE (FORM FLAG)
( PROMPT-AND-READ ' : EXPRESSION-OR-END
"Macro form -♦ " ) )
UNTIL (EQ FLAG ':END)
DO (LOOP AS NFORM = FORM
DO (SETQ FORM (MACROEXPAND- 1 NFORM))
UNTIL (EQ FORM NFORM)
DO (PRINC " -» ")
(GRIND-TOP-LEVEL FORM))))
(DEFUN MEXP-all ( )
(LOOP WITH (FORM FLAG)
DOING (FORMAT T "~2&")
(MULTIPLE -VALUE (FORM FLAG)
(PROMPT-AND-READ ' : EXPRESSION-OR-END
"Macro form -» " ) )
UNTIL ( EQ FLAG ' : END )
DO (PRINC " ■+ ")
(GRIND-TOP-LEVEL ( zwei : macro-expand-all FORM))))
Chapter 5
THE GRAPH EXAMPLE
This chapter, rather than present some abstracted features of the lisp language or
of the lisp machine operating environment, will cover a programming example
which puts to use many of the features we have previously discussed. The piece of
code in question allows one to display and manipulate simple undirected graphs,
that is sets of nodes connected by arcs.
If your site has loaded the tape which accompanies this book, you can load the code
by using the CP command Load System graph [or evaluating (make-
system 'graph)]. Once the code has been read, start the program by evaluat-
ing (send (tv: make -window 'graph-frame) : select).
Please refer to section 5.5, which contains a picture of the program in operation
and a listing of the code. The first three sections will point out and briefly discuss
the interesting features of the code. The basic mechanism is contained in the first
half of the file. All the graph manipulation functionality is there, provided you're
willing to type in awkward forms to a lisp listener. The rest of the file provides a
more convenient user interface.
84 THE GRAPH EXAMPLE Chapter 5
5.1 The Nodes and Arcs
The four defvar's
These four declarations are for global variables that will be needed at various
places throughout the code. A defvar must precede the first appearance of the vari-
able so that the compiler knows the symbol refers to a special variable. Another
good reason for putting them at the beginning is so anyone reading the code can
quickly find out what hooks are available for modifying the program's behavior.
The node defflavor
All five instance variables are sellable (you may recall that settable implies get-
table and initable). The : required- init-keywords option specifies that
every call to make-instance must include initial values for xpos and ypos.
defun-method
This facility is sort of a cross between defun and defmethod. It's used for defining
functions to be called from inside methods, which need access to the instance vari-
ables. For defun-methods to be compiled optimally, they should be defined before
they are used.
The : init method
The last thing make-instance always does is call the flavor's : init method, if it
has one. Here you can specify operations to be performed on every instance of your
flavor, upon being instantiated. I use this one to set the radius of the new node
according to the size of its label, and to add the node to the list of all nodes. Note
that many of the window mixins already have : init methods, so if your flavor is
built on some of them you'd better use a before or after daemon rather than a pri-
mary method so you don't override the other one.
ignore as an argument
Use of ignore in a lambda-list for an argument which isn't going to be used
saves you from getting a compiler warning about an unused variable.
: send-if-handles
This method comes with si:vani!la-flavor. The specified message is sent only if the
object has a handler for it. This avoids unclaimed message errors in cases when
you can't be sure of the exact flavor of the object you're dealing with. The
Section 5.1 The Nodes and Arcs 85
: primitive-item message has to do with mouse-sensitive items. We'll get to
that a little later.
map-over-arcs
I wrote this macro to have a handy way to iterate over all the arcs. For each node
it runs through all its arcs. If it has already seen that arc it goes on. If it hasn't, it
executes the body forms with the specified variable bound to the arc, and marks the
arc as visited. Note the use of gensym to make sure the mark-var and node-var
don't shadow any variable bindings.
5.2 Managing Multiple Windows and Processes
We now have everything needed to make and alter graphs, but it would be very
awkward to do it by typing forms like
(make-instance 'arc
model (make-instance 'node :xpos 135 :ypos 251)
:node2 (make-instance 'node :xpos 338 :ypos 92))
The rest of the file is concerned with making it easy to do this sort of thing. First
we define a frame (an object composed of several windows). Our frame has two
panes, one for display of the graph, and one for typing lisp forms. Then we set up
a process in the graph pane which just watches for mouse clicks on the mouse-
sensitive items (the nodes) and dispatches appropriately. The lisp pane, by virtue
of being built on tvtlisp-Iistener, will automatically have its own process for reading
lisp forms, evaluating them, and printing the results.
Managing multiple processes is something people often find confusing, at least the
first few times. Windows, processes, and i/o buffers are all independent objects on
the lisp machine, so you can have nearly any number of each. A window can have
zero or one processes running in it, and any number of processes can exist indepen-
dently of windows; windows can each have their own i/o buffers, or any number of
them can share a single i/o buffer; any process can read from (or stuff characters
into) any i/o buffer. How do you decide how many you want? I'm not sure how
generally applicable they are, but here are some guidelines you can try out.
First decide on the windows: the number of windows is your number of distinct
output channels to the user, so you'll need as many as you have things you want to
display simultaneously. In this example we want to see the graph and our
86 THE GRAPH EXAMPLE Chapter 5
interaction with a lisp listener, thus we have two windows.
Next think about your input channels from the user. You'll probably want to use
one i/o buffer for each. In our case we need one for keyboard typein (lisp forms)
and one for mouse clicks on the mouse-sensitive items. Hopefully it will be obvious
how the i/o buffers and windows match up. For this example it is: we'll use the i/o
buffer of the window displaying the graph for mouse clicks, and the i/o buffer of
the window displaying lisp interaction for keyboard typein. (See chapter 7 for a
situation that calls for having two windows share an i/o buffer so that the process
running in one can see input from both.)
Now processes. For each i/o buffer there will have to be a process, generally one
running in the window to which the i/o buffer belongs. (Having several processes
read from a single i/o buffer would lead to undesirable "race conditions," where the
program's behavior randomly* depends on which of the processes happens to get to
a particular input first.) If there are leftover things to do which don't involve direct
communication with the user, you can create extra processes running without win-
dows.
In deciding how many i/o buffers and processes you need, keep in mind that some
tasks will be handled by the mouse process, for free. Think back to problem set
#2, problem 4, when you defined a : mouse-click method which drew a square
where the mouse was clicked. That window didn't have a process in it, and you
made no use of its i/o buffer. It was the mouse process that watched for user input
via the mouse and called your method when appropriate. Similarly, in this graph
example, we'll define a : mouse-click method to create new nodes at the mouse
position (see below), and this will happen independently of our own processes and
i/o buffers. The mouse process does it all. There are other tasks, however, for
which the mouse process only does some of the work. It turns out that handling
clicks on mouse-sensitive items fall into this category. The details are covered later,
but what's relevant here is that when a mouse-sensitive item is clicked on, the
mouse process simply stuffs something into the i/o buffer of the window under the
mouse, and does no more. Somebody else has to notice what's in the i/o buffer and
do something about it. That's why the window displaying the graph needs a pro-
See hacker's definition at end of chapter.
Section 5.2 Managing Multiple Windows and Processes 87
5.3 The Windows and the Mouse
The graph-frame defflavor
This defflavor uses the :def ault-init-plist option, which lists alternating
keywords and values. The effect is as though the keywords and values were present
in every make-instance for this flavor (unless explicitly overridden). The
: selected-pane init keyword means that whenever the graph frame receives a
: select message, it will pass it on to the the lisp pane. The : panes keyword
says that this frame has two panes, named "graph" and "lisp," and that they are
instances of the flavors graph-pane and tv:lisp-listener-pane, respectively. The
: configurations keyword specifies the layout of the frame. Configuration
specs are often very messy, and you should try to read up about them. This simple
spec states that the graph and lisp panes are stacked vertically, with the graph pane
occupying the top 60% of the frame's area, and the lisp pane occupying the rest.
Selection
The next two methods, :alias-for-selected-windows and
: selectable-windows, are ones which applications programmers don't usually
mess with. Although what these particular methods do is easy to see, the manner
in which they affect the behavior of the Select key and system menus is extremely
obscure. The only reason they're here is because I thought it would be nice if we
could use the lisp pane to make our frame accessible via Select L. Take a look at
the comments in the code, and don't worry about it too much if you don't com-
pletely understand. (Incidentally, the bug in the speech editor which I mentioned
in the introduction to Part II of the second problem set had to do with these mes-
sages.)
The graph-pane defflavor
This flavor is based on graph-window (defined below), with tv:pane-no-mouse-
select-mixin added. tv:pane-no-mouse-select-mixin itself is just a combination of the
flavors tv:pane-mixin and tv:dont-select-with-mouse-mixin. The former provides the
functionality a window needs to be a part of a frame. The latter fixes it so that a
pane won't show up in various system menus. Otherwise those menus would con-
tain separate entries for every pane in every frame, which is most inelegant. All
you really want is one entry for each frame. (As to exactly what that one entry
should be, see the previous paragraph.)
The graph-window defflavor
This is the definition of the window in which the graphs are to be displayed. It
88 THE GRAPH EXAMPLE Chapter 5
includes tv:process-mixin so that there will be a process running in the window, and
tvtbasic-mouse-sensitive-items, so the window can display items which will be mouse
sensitive. We use :default-init-plist again here. The : process key-
word gives the name of a function which will be called on one argument (the win-
dow) the first time the window is exposed. Such functions are typically written as
loops which never return. The : item-type-alist is an instance variable con-
trolling the behavior of the mouse-sensitive items. The contents of this list are
described below. The :blinker-p keyword specifies that this window is to have
no blinkers, and the : font-map keyword is a list of fonts this window is to start
out knowing about. In this case, the window's sole font will be Helvetica, 12 point
italic. (The default font we're all so familiar with is called "cptfont," so the font
object may be found as the value of the symbol fonts : cptfont.)
The : init method for graph-window
This is where the variable *graph-window* gets set. Note that this setup assumes
there is only one active graph-window around at a time. Whenever one is created
the old binding of *graph-window* is lost, and anybody trying to use the old win-
dow is likely to get confused. (If you wished to have several graph-windows around
simultaneously, you'd have to think of something more clever than a single global
variable to keep track of them. Similarly, the global variable *a!l-the-nodes* would
have to go if you wanted different graphs in the different windows.) Note also that
this is an after daemon, to avoid clobbering the important : init method defined
for tvrsheet.
The main-loop for graph-window's process
This loop simply reads blips from the window's i/o buffer and dispatches. The only
blips it's expecting are ones with a first element of : typeout-execute. These
blips are generated in the mouse process when someone clicks on a mouse-sensitive
item. The remainder of the blip will be a function name, dependent on the type of
item and type of click, and the item itself. This loop simply calls the function with
two arguments, the item and the graph-window. As we'll soon see, the items will
be nodes, and the functions will be ones like delete-node, rename-node. etc., all
defined below.
The : refresh method
This generates the picture. It sends each node and each arc the : draw- self
message. (Note the use of map-over-arcs.) This is an after daemon so we don't
override the : refresh method of tv:sheet.
Section 5.3 The Windows and the Mouse 89
The :who-line-documentation-string method
The response a window gives to this message is the string which is displayed in the
mouse documentation line at the bottom of the screen whenever the mouse is over
the window. : override is a kind of method combination we haven't discussed
before. It is similar to : or combination in that if it returns nil other methods get
a chance to run and if it returns anything else they don't. It's different from : or
combination in that some of the remaining methods may be before or after dae-
mons, which isn't allowed with :or combination. In this case, the intent is that if
the mouse is not over a mouse-sensitive item, the string supplied in this method
should be displayed. But if it is over a mouse-sensitive item, some other method (in
particular, the one on flavor tv:basic-mouse-sensitive-items) should be allowed to
specify the documentation string.
The : mouse-click method
If the mouse is not over a mouse-sensitive item, and if the click was a single one on
the left button, make a node at the mouse position and display it. Otherwise let the
other : mouse-click methods have a chance. (Recall that : mouse-click
methods have :or type combination.) The new node will have no label.
The guts of the mouse-sensitive items
For each type of mouse-sensitive item a window knows about, there is a set of pos-
sible operations. (Graph-window currently knows about only one type of item, the
:node type.) The item-type-alist (recall that this is an instance variable of
tvrbasic -mouse-sensitive-items) tells what the possible operations are for each type,
and also indicates that one of them is the default operation. The : mouse-click
method for tv:basic-mouse-sensitive-items is set up so that if you click left over an
item, a : typeout-execute blip is forced into the window's i/o buffer, listing
the default operation and the item itself. If you click right, it pops up a menu with
all the operations for that type of item, and when you choose one a : typeout-
execute blip is sent containing the chosen operation and the item. It's up to the
process running in the window to read these blips from the i/o buffer and do some-
thing with them. As we've just seen, the process in the graph window calls the
operation as a function, with arguments of the item and the window.
The internal structure of the alist is a bit of a mess, and it usually is not built by
hand. Instead, it is generally constructed with the macro tv:add-typeout-item-type,
which is called once for each operation defined for an item. The first argument is
the alist to be modified, the second is the type of item, the third is a string to name
the operation (this appears in the menu you get from clicking right while over an
item) and the fourth is the symbol which actually ends up in the blip if this
90 THE GRAPH EXAMPLE Chapter 5
operation is chosen. The fifth and sixth arguments are optional. If the fifth arg is
t, the operation becomes the default operation for this type of item. If the sixth
arg is present, it is a documentation string to appear in the who line while the
mouse is over this option in the click-right menu.
You can see that for the graph window the default operation (which you get if you
click left over a node) is the function mouse-new-arc. If you click right you get a
menu listing four other operations in addition to "New Arc," for deleting an arc,
moving a node, renaming a node (new label), or deleting a node.
The function definitions for the operations
All that remains are the definitions of the five functions which implement these
operations. The first two (mouse-new-arc and mouse-delete-arc) use a function
from the Edit Screen menu to choose an arbitrary point in the window, then see if
there's a node under that point, and if so, act accordingly. The delete-node function
is very simple. The rename-node function uses a built-in function named tv:get-
line-from-keyboard which pops up a small window and prompts the user to type in
a line of text. The function for moving a node uses the same piece of Edit Screen
as the earlier two.
: item and : primitive-item
The only thing I haven't explained is how the window knows that some portion of
its display is supposed to be a mouse-sensitive item, and of what type. The : item
and : primitive-item messages take care of that. If you look back at the
: draw-self method for node, you'll see the : primitive-item message we
glossed over before. What this one does is tell the window that there is an item of
type mode, with left, top, right and bottom edges as given. The window has an
instance variable which is a list of all the current mouse-sensitive items. The effect
of the : primitive-item method is just to push the given item on the list. The
: item method (which isn't used by graph-window) is an alternative to
: primitive-item for mouse-sensitive items which are text. The method will
take care of printing the item and figuring out its edges. With graphical objects
like our nodes it can't do that, so we display them ourselves, calculate the edges,
and send the : primitive-item message. To see whether there is anything on
the list at a given position, send the : mouse-sensitive-item message with
args of the x and y coords. If there's an item on the list matching those coords,
some information about it will be returned, including the item itself, its type, and
its edges. This method is primarily for internal use by the : mouse-click and
: mouse-moves methods of tv:basic-mouse-sensitive-items, so the window knows to
highlight the appropriate region when the mouse is over an item, and what to do if
there's a mouse click.
Section 5.3 The Windows and the Mouse 91
5.4 Fun and Games
From The Hacker's Dictionary, Guy L. Steele, Jr., et al:
RANDOM
1. adjective. Unpredictable (closest to mathematical definition); weird. "The
SYSTEM'S been behaving pretty randomly."
2. Assorted; various; undistinguished; uninteresting. "Who was at the confer-
ence?" "Just a bunch of random business types."
3. Frivolous; unproductive; undirected. "He's just a random LOSER."
4. Incoherent or inelegant; not well organized. "The program has a random set
of MISFEATURES." "That's a random name for that function." "Well, all
the names were chosen pretty randomly."
5. Gratuitously wrong; poorly done and for no good apparent reason. "This
subroutine randomly uses six registers where two would have sufficed."
6. In no particular order, though deterministic. "The I/O channels are in a
pool, and when a file is opened one is chosen randomly."
7. noun. A random hacker. This is used particularly of high school students
who soak up computer time and generally get in the way. The term "high
school random" is frequently heard.
8. One who lives at Random Hall at MIT.
J. RANDOM is often prefixed to a noun to make a "name" out of it (by
analogy to common names such as "J. Fred Muggs"). It means roughly
"some particular" or "any specific one." The most common uses are "J.
Random Loser" and "J. Random Nerd." Example: "Should J. Random
Loser be allowed to delete system files without warning?"
5.5 The Program
92
THE GRAPH EXAMPLE
Chapter 5
Section 5.5 The Program 93
a>
-0
0) 0)
•0
a>
A tj
0
!*
id 0
^»
Pi
id
rH C
rH
rH
ft
0 4H
0)
A 0)
P
CO
Pi 0
m TJ
0
•H
CO
rH 0
>
T>
A U
H
Pi
Id
P 0)
0)
4H
rH
CO
•H P
u
X
0 0)
4-1
•H
> 0)
5
rl
ITJ
■H
ft
43
43 -P
4-1
43
CO -H
P
0
Id
01 P
tj o)
u
■H
tn Pi
CO
P
O ft
0
0)
a>
Cn
a
>
X
rH TJ
CJ
TJ
ITJ
CD
0)
fi
a>
4H Pi
rH
n
Pi *
id
A
0 id
4H
0
0 id
•p
P
a
rH
CO
«-» rH
4H
P ft
c
.ci
CO 0)
0
4H
Pi CO
«-~
•H
o
•H
rH £1
0) Id
to
0
0) -H
TJ TJ
CM
^^
XJ
X rH
ID
P
Pi
^->
1
0)
*
•H
0
<v
0) 0>
rH
1
>
•H
-P
■H
ft Pi
o>
PS 0)
(TJ
P
p
C
0)
ft A
0>
TJ 0
0)
A
id
o
CJ
•H S
CO
o
— P
rH
*~
CO
id
8
— V
0)
rt
•rl
4H
CO -
1
• •
£
•a
0) J3
0
rH CT>
0>
a
4-1
p:
N
4H
0) Pi
CO
id
0
•rl
•rl CO
0
CO
X -H
Pi
3
P
»
CO rH
TJ
•H P.
0)
b>
-p
0)
p
P
ftp
rH
CO
01
0) X
CO
0
CO
1
• »
•a
•H
.C
J3 -H
•H
0
Pi
&>
a
0
■p
rH
-P
•P ft
rH
0
•rl Id
•H
co
o
P
o
0)
P
•H
P
CO
V
TJ
o»
§
*
•d
S
*
0
o
4-1
TJ
fl
0
^x
Pi
&
>,
*
CO
*
„
•H
• •>
<«
CO
0
CO
ID
1
ft
rH
n
R
0
N
43 —
CO
a
•rl
•H
•H
ft —
H
CO
TJ
«-»
TJ
CO
id —
J
■H
tj
O
id
rl
CO
w
id
rl
P!
P *
C^ CO
• •
T-
1
a
CD
1
•H
* Pi
01
•0
0)
CO
T)
ID
tn
•H
*
c
* «■»
TJ
0)
X
0
TJ
p
TJ TJ
*
CO CO
0
<-i
C
0
m
Pi id
s
c
rH
•rl
rH
3
•H *
Pi
1
A
id
CO
CO
c
1
a
i
01 U
CO 1
• •>
0
CI
•H
TJ 0)
1
■rl
Tl
PI
1
CD
^ 0)
A
•rl
Pi
(0 N
rl
p
•rl
TJ
TJ
en
p
*
P -H
a
id
0
T)
s
0 \ 0
•H
id
CO
*
1 CO
•H
>
K
id
■H
Pi \ Pi
rH
0)
0)
s
0) 1
Pi -»
i
rl
c
*
(0
P
•d
0
T> Pi
•H — »
0)
0)
•H
~s
P
O
0
•a
0 -H
*-*
B rH
O M
P
a
+
01
a
a
Pi &>
rH
* -H
Pi
1
CD
*
*_-
a
N
P
i
•H
1 rl
•rl
Pi
id
p
CO
r-i
•H
(0
0)
>
e (0
c
CO
p
•H
<D
CD
X
Pi
■C
1
3 e
0)
P> rH
CO
PI
rl
X3
id
•H
k
CO
■p
43
6 i
■0
CO
•rl 0)
a
•H
CO
id
a
a
id
ca
ft
•H 0)
0
u
CO
CO T) A
■H
T)
P)
rH
*
P
ft
rH
(0
Pi tj
C
P
0
0 Id Id
|
T)
0
•H
g.
rH
rl
■H 0
aJ
ft
ft P. rH
0)
<D
*
TJ
4H
Id
0>
e p!
rl
X
>i~ ~ -
• i-\
P
P
m
•H
CO
1
4J
CO
P
*
rl
p
rl rl
0
>
id
~
■ A
ITJ
P
•H
0)
e
i
u
^
*
•H
ITJ
Id
•0 id
rH
P
0)
p
1
4-1
>
4H
>
4H
> >
4H 4H
4-1
4-1
0)
CO
P
4H
CD
CO
0)
0)
0) 0)
0)
ID
1*
1*
•0
■0
TJ -0
n
TJ
94
THE GRAPH EXAMPLE
Chapter 5
■rl
+J
"O
45
(0
0>
h
■H
CD
P
••»
45
G
>
1
0
0
CD
«w
■0
c
C
•H
P
•H
rH
a
>
••
0)
u
A
S
u
ft
0
a
id
73
-^
o
P
d>
•H
CO to
CD
s
3 3
43
0
•H -H
P
C
-d
C
■d -d
<d id
a
CO
CD
u u
•rl
•rH
(0
""
Q>
*^
to to
0 0
P
ft
_»
££
£»
A
1
fc
10
P
>>
w
1 +
rH
0
4H
•H
CM
CO
»-- "^
(V
rH
£
EH
r-\
ft
^ -^
A
•H
CO
CD
CO CO
p
A
»
0)
rH
i
42
03
,
•H -H
>4-l
CO
■ — •
-d -d
0
-d
^
CD
CO
Oi
^^
id id
u u
43
p
CD
P
E-<
fJ
C
CO
P
<d
P
0
Cfl
■H
•H
3
to to
0>
0)
U
I
C^
h
0!
P
P
•H
■d
aa
(V
o
•H
J
P
CO
03
X X
-» rH
-H
w
1
rl
V
CO
^^
.-»
a
w
<-^
CO
■d
1 +
P CD
i
03
>
0
CD
CO
O A
»-»
CO
<D
.— -
0
ft
M
0
*~.
&*
CD
rH
3
P
.-»
Eh
■d
s
CD
ft
<4H
o
■o
CO
CD
•H
P
(J
c
P
X
t-i
p
•H 0
o
0)
a
■a
CO
XS
W
•H
CO
C
CD
.-^
id
P
c
-0
id
<d
CD
b
5
0
CD
+
CO
to
U
■P
0
^
rH
p
6
m
^^
^^
ft
O
—
CD
P
*^
10 (J>
c
03
o
X
rH
CD
03
<l) C
*
&»
*
P
p
*+H
C
i
e
^-~
^-«
>,
-^
■d
■d
O "~
P -rl
s
c
^
rH
CO
CD
CD
rH
g
2
03
-^
4H
CD
03
CO
a
O
P to
cd T>
0
•rl ~
rH
CD
CO
CO
CD
I
CD
E
rH
r-{
r-i
s
03
a
id u
~ P
•d
c —
-—
Id "O
CD
CO
r
P
03
CD
O
ft
■H
45
O
1 U
O
c
P *4H
CO
0
P
I
P
CD
CO
P
CO
■d
i
P
CD 03
*->• o
■H
rn
fd rH
rJ
4H
a
P
P
rH
CO
U
1
•H
•H
<d
4H
a
03
>
P o
»
•H
S CD
•H
o
1
CD
C
CD
p2
P
*
O
Tl
P
•H
«
0 O
•h id
1
C
= to
■d
CD
P
*
•H
45
i
CD
CO
03
1
1
p
■d
a p
C
^
43
<d
p
A
<4H
*
P
03
CD
^
rl
S
CO T3
•H
T)
oi id
•H 10
rH
ft :*
p
CO
P
<d
0
ftrH
2
CD
T3
03
?
a
(5
03
p
•• 3
(1)
id
IW
•H
1
T!
ft H
C
a
rl
0
CD
CD
<^
.. o*
•H
JQ
P
•H
p
rH
rH
C
P
1
Eh
03
T3
-d
x
CO
>
»-»
rH
<U T3
0)
d> P
CD
rH
CD
•H
CD
0
>» 2
u
a
CD
c
■H
CD
CO
CD CD
■d (0
rH
*
0
CO
CD
<d
-0
*
T3
m
H
C
■d
•H
i
P
■d
u
-d -d
0 P
c
«D
A
*
0
1
0
•H
Pi
■H
o
0
s
s
»
•H
0
rl
0 —
G
-P
P
P
P
c
A
C
CD
45
Cu
P
C
c
0
0
a
(5
03
C
*- <u
o
0
>
4H
ft
e
CO
ft
■H
T3
rH
T3
T3
•H
— CO
A
c
Pi-
p
0
rH
05
0)
03
H
u
P,
CD
fi
C
P
CJ
O
•g^
P
p
CD
■d
P
•0
c
rH
W
ft
X)
•H
J3
CD
H
ft
>d
U
T3 P
CO
0
tn
0
CO
0
S
03
CO
£
0
03
0 id
A -p
Tl
43
45
^
45
rH
■~^
45
45
-P tt) -0
TJ A
p
P
4H
p
■d
T3
P
45
p tr
0) CO
c
<d
CO
CD
13
CD
p
•H
CD
c -d
C
CD
CO
Q) +J
B
0
3
e
£5
6
CD
^
e
CD
C
CD
e
El
a oi
<w •►
o
.-
a
MH
03
14H
r-i
14H
CO
01
CO
4H
ft
MH CO
0) .-
CD
(1)
CD
CD
0) ^
■0
T)
T)
T3
13
■d
Section 5.5
The Program
95
•H P
a a)
(V
— .c
v m
p ••
0) >
rH P
a)
■?.§
M
•d
o -d
£§!
o
■d u
o n
.£ o
p .Q
0) «^
e
4-1 •♦
0) .-
T3
a> in
> ~ a;
0) to *d
M ~ O --'
ft CO u
O (0 *
O fc o
** «3.g
^ J u o
(0 J — c
> p I
Z P a)
w ~ Z .£
•H W P
,C J W I
Eh H
o cu a*
q o -P
o a>
0) <D
■d -d
o o
fc TJ -d
moo
e c c
S-l V4
fl Hi
O ~
>
id
a> a)
•d -d
o o
0) OJ 0)
H rH rH rH
■H .Q jQ £1
C <TJ it} rtj
-p +J p
P -H P
Q) (5 0)
Ill-ri (Jl
96
THE GRAPH EXAMPLE
Chapter 5
^
^
CO -H
^-^
^
P) tn
ft
^
•H £3
CO
J3 -H
•H
c
■P "0
rH
0)
>
■H
3 P
.C
0)
0 U
ft
C CO
<fl
>
P
ft
>» 0
tnvo
CO
CO
•H
>! CO
§
o
rH
p
■p id
3
£
o
rH
ft
C
CO CO
0
id
0)
-H >
■>-»
O
rl
^1
CO
tn -P
w -o
p
»■»
o
$
»»»
—~
c
co A
>
1
P
0)
c
•H
JCJ -M —
■P CO •
<-» M
id
*-»
c
l
e 4h
CO P
>
CN
m
CO rH
Sg
i
CO
ft
CQ CO CO
«
0
i
P
3 .C to —
(0 •>
p
&
p
cu
C!
CO
0)
Id en -P —
O >- -H
-—
e^
c
* N
Q) -»
P *
CM
0>
id -h
A CO CO
(0 u
CO
p
rH
CO
• e 3
^
M
a
, — i
0)
CO
- co id 0
p
--^
C
■H
>—
—
J S P -0
Q> ..
?
X
«
rH
i id <m a
*-.
■0
-^
-^
ft
1
c
•P P 1 -H
*
0 u
CN
►,
^
1
ft
•H
O 4H A >
I
G <d
-p
P
-a
^
CO
<fl
CO 1 ft 1
* - >
a>
'—
-~-
.— »
^~
&
ft
■H
e
rH J3 Id TJ
to 1
co
>.
CO
*
rH
CO ft P CO
CO TJ O
CO
-o
A
A
*S
X!
-—
■H
P
^.^
co id tn -P
*
•o a u
o oi («
0
P
P
P
+J
(D
rH
tn
>
«.
P u
p
ft
*
tn
tn
tn
0>
e
*
P
id tn co co
a co -
03
a
c
C
C
nj
A
CO
•H JC rH
ja
i ^
>
OJ
0)
cu
CO
n
CU
ft
ft
C
> id -p co
CO T3
1
rH
rH
rH
rH
4H
C
rtJ
CO
0
CO
~ R
5 a c
o
CO
s
|
(0
P
■H
•H
CO 4H (3 1
■P -H CO
M
0
-P
ft
tn rH
P
rH 0 P P
P CO
1 CO
a)
ft
T—
T—
<N
CN
c
1
^^
^—
rfl
43 3 0
to a
rH P ~
X
3
M
P
P
U
•H
TJ
P
•H -O -P 4H
> V
rH Id
>,
m
0)
%
g.
to id co i
1 tn
m > O"0
t—
3
>>
3
>.
p
P
CO CO P CO
o •—
* 1 CO
0
C
u
*
13
■o
p
U
CO
•H
co p» «d
p "»
o *-
a
9
^-*
—^
-^
CO
d)
<D
4H
O tO -0 -H
03 - — - — It
G P
©
co
T—
T—
*
*
*
*
C
rH
c
c
O CHH
•H Id CO
CO
CO
CO
+
0
0)
rfl
0
id -h 3 id
"* & £. *
« CO
P CO
0
CO
a
— '
N,
N.
s
\
3
o
CO
a
u
0 ••
CO (0 CO >
id M h
■d
0
X
p
V
\
s
\
T3
e co co
U C C I
> 0 c
I 4-i 3
c
p
0)
<1)
P
id C TJ 6
P 0) 0> X
1
CN
(N
V
6
P
CO
p co c id
id tn tn P
CO
4-1
to
CO
CO
r—
T—
CN
CN
s
CU
•H
4H -P Id P
rl~~§
-0 ft
0
0
■H
CO
CO
CO
to
u
T3
rH
0) 4-1 —
0 0
1
ft
«—
a
0
0
0
<u
P
a
P -H CO 1 CO
11 h VI •
£5 0
CO
X
s
ft
a
i
a
0 r-t e xi g
> id id
- rH
CO
,C
X
X
A
-p
0 i id ft id
O > > A
-
tn
I
1
•P
ft
•H
ft P id ft
1 1 I +3
u
-0
tn
+
+
id
>
c
tO tO 4H P 1
ft * <u -h
0 0
CO
C
^
^
^
w
u
p
■H
co -h i tn "0
A p tj s
SISa
4-1 >0
1
4
>1
CD
Cn
1
XH O- 4)
TJ
T3
rH
CO
P
Id -rl -P
Pi
• — ■
a>
M
rH
B id to no o
o «- — 0
•H
3
0
3
id 0 co
P — 0
4-1
rH
>
<fl
tO tO XX A rH
O rH
*
(fl
(0
4-<
•H <■ +J CO
IS r
S
+j
>
rH
CO
a v a co to
<u
4H
T3
£-1 -rl 0 S ••
4H H
4-1
rH
4-1
4H >
(U —
CO
CO
•- •' •* CO -P
•0
•o
•o
n
Section 5.5 The Program 97
ft
o
p
TJ 0)
+» <M 0) >
(OOP
CO TJ
CD P -H O
(0 -H +J
AH « «
c s
ft « s
m^ ft (I
•HP 43
rH ft £<
CD CO ^
CD ^ -H
43 0J rH
P 6
0) 4J
CD CD 43 -H
CO M -P —
03 -H tO «-«■
h mi —
>> > x v
CD CO <0 -H C
>! &>43 >M <0
C ft
P -H Tj CO I
O 43 rH -rl TJ
CD P C 43 CD U
H O tn P
a; co £ u
CO 0) CD 4-1 it!
O P • rH II
CD TJ H — CD — HO)
43 a» co — a> ft
p 43 e •• * > £
O • 10 > O 0) P <—
c; 'H h ^ +j nj. i — i i 4»
>! 43 C «H - C Ig ri
<tj>a>i -h ft cd o —
S 6 43 — * OP — 2 -H
S ft C I +» -H — & >,
O Jl g fl -O 43 II— -H — P
+j +j CD P -- •* ft J43-H > I
COP&lp (0 OftCNP O r^
-O >n co — o P tjifl'-co ■d C
•rl CO >> CD — CO CD t? C P rH CD «3 <0
>C CO 43 •> rH CO -rl fr 43 P -rl
>p?ocd i3 e>*--us >
POCDOTJCO -H CD I CO -^ ^ ri IH^
CO TJ 43 P -d C I X +J43+JP -rl rH CD
C C P O C -H P -H -H ft CO C — C C CD P
•|-| -rl <M -rl > O 6 I (0 -rl rH O P O CO 0 —
S <M > I »4H I CD P rH -H 4H -rl -H —»0 —
CD O 111 I 91 I P > &> <0 C >- C P ft T> CD —
S CD O rH rH CD O -H •— I » -H O O C X 4H
43 C C <0 43 6 CD P * CD ft •• £3 OCDCDrH
&HJ O O -H fl fl rH -H ftlft C rHCOICD
C -rl p p C a, CO CO >, p <0 P 4H I >- P CO
•HM-tP-OCO" co C copcdB CD i (3 3 _
43 O ft C CD CD I CD CD I ,!«! I -P — • rH -HMOS
■p o id n h ai cd (3co o g c p mh— <d <o cdcd
SrH
CQCDC3 CO -HI O CD -rl C <0 4H >
p p <W CD CO <0 S *-»XCD h+JrHO •• H CD
•e.-££.t
4*1 <0 O rH •• ft O -'■H « ft -H 43 4-1 CD rrift CDP
<0 ft CD CD C I ~ 6 S C >C0 10 * P •• ft
CD rH CO O CD TJ «- I » I O O ftOO'HO
C CD CD P BCD O O CO B — P TJ* Ori T3 TJ
CO 43 CO -rl CO (0 P CD C >0 CO I ? CO C ? PI G ft 5 rH
+J -H p O CI C3CDOO-H -HO I C -HOSrH
CD CDP *M CD 03 (D •HU-H'OrH ? T3 5 -H > 0) n3
43 CO 43 O C* I rH ft C £ O CO C ft IC O <0 1-0*0
En CD P <4H C 43 CD 1(0 I P <fl -rl I 43 -ri TJ 6 43* C
co -Hftco co, 43ft4a*p ft> a •■ ftScrB
C P CD -d «J •• ft •• ft H <fl I -H fl CD CD <W
•»wOO-HP> rfl> «>>>C P43 >S P >J ^ —
>!CMHCP&)P PP U *J *J *J -r^ &>ft 10 C7> —
CQOOP^ tp^tji^ I ^(0 43"O^CO
id O 10^ 0) TJ P pftC PCDTJ
>>'OCP P r-i 'O&i<0-HTJO43
prHOCDOOCD O O C O* P» Oiw?
x CD-OO43c0> > id 43 tj>43
C>C-HCDP^m nJ *w PC -OPft
OCD-H^PCD-rH rH CD CDPCCCDO
QrH^plCB— <W IH T3 BCDSCDBO
4-|x_,l4H IW •• IMCO^HCOM-lrH
.-.».».» .^ CD ' CD CD •-' CD — ' D w D —
. ^ . ^ .«-.«-.*- rrj frl irt trj irj tr)
98
THE GRAPH EXAMPLE
Chapter 5
P
■H
o
u
0)
to
rH
•H
0)
p
e
rH
•H
rH
•H
(3
p
a)
o
u
c
c
CI)
rH
p
0
■d
tt)
03
>
0)
0)
o
■d
o
-d
o
-d
0
13
i ■»■»
0 B
0)
c
(3
c
0)
(0 •
(3
V
1
i
>
rj rjj
I
43
CD
~~-
d)
0)
o
0 4)
tt)
rH
»
N
0)
p
g
e
e p
to
■H
•H
N
0)
tt>
3
C ~
d>
to
•H
rH
c
0)
= rH
0
13
1
(0
0)
0)
to
O 0)
a
~*
•H
c
1
■d
p.
3 ~
p -d
d> i
43
•H
c
0 =
<
13 a>
■P
Cn
•H
e •
0
o ^
•H 10
0
p
tJ>
0)
*-»
0)
--~
«
4) 43
r.
P 3
C
03
M
p
s
S
=
= d
P
P o
e
03
v
03
cu o
tt) 0
o
to E
0
i
e
rH
43
(3
0)
> C
rH P
» p
i ••
•d
■p
i
d)
ft
CU
d
0
0
tt) 03
C >
4H
ft
o
03
Pi
0
S to
Q O
21
O P
0)
0
P.
c
= -rl
= u
= >
■H
rH
p
Cn
43
0)
0
9)
P X
0)
0)
CO
0) jj
0
tt) 13
u
(0 1
>
>
T3
d)
d
•H
d
-d c
•d
O
P tt)
P
p
0 43
0
43
0 P
O 03
O 03
g>
a to
--n
-■*
(3
p
c
P
c 0
{3
13
0) 3
=
>»~
X
>^
•• 4H
.. m
.. m
•H
IS
3
4-1
P.
0
0
c
X P
1
i
*
0
*
0
* a
*
■P
O ••
0)
w
_-
P
p
4-1
P 0
p p
p p
CO
0 >
E
to X
to
p
to
tO -H
to c
to fi
0
*d P
C
to
to
•H
3
•H
rH
•H P
•H -H
•H -H
U
1
e
O S
a
o
rH
0
rH
CD
rH -H
rH O
rH O
<4
oj e
CD
P W
ft
03
03
42
o) to
03 ft
03 ft
c a>
p
P E*
X
1
0)
1
03
1 O
1
•H P
(0
3 H
tt) T3
0)
rH
V ft
tt) -d
tt) -d
rH -H
:*
43 I
ft 0
ft
ft
ft (3
&s
45
I |
en
~ W
cu
>> «3
^
IS
>^ >
>, tt)
CO ->
o a)
>
xs
P
P
<1>
P tt)
P
+i
0) ~
43 >
^ H
0
I
to
£3
1 c
1 0
1 0
u <m
> -H
-X Eh
c
6
•H
e
e
e c
e c
«H rH
.. +J
CN
O H
0) 43
0)
03
tt) 03
tt) 0
0 0
tt) * tt)
•H
«
•H CO
>4H
r-i
P
P
p
p
p
p
h to n
a> to
rH 55
0)
rH
•H
•H
•H
0)
•H >,
•H >,
•H >,
.. o>
■d (3
u w
o
0)
fi
1
0)
1
d
1 4-1
1 4-1
1 4-1
•d mh
•H 0)
1 CO
c
to
43
U
43
•H
43 -H
43 -H
43 -H
P O rH
—.
P tQ
0) 1
03
*
ft
•H
ft
>
ft o
ft o
ft o
tt) J3 tt)
^~-
P 1
to w
-^
-p
l«
+J
03
rH
05
0
03 d)
0) d)
03 tt)
P 1 (0
4H
a) a>
3 10
to
t-t
m
U
ft
U
u
U ft
P ft
P ft
<U 0 1
rH
> to
*>
0 D
I
c
0)
■H
& CO
d>o<
0>CO
0> to
CP (0
03 43 *
0)
0 0
<d
e o
<H
•H
n
rH
*
:
*
=
* s
* =
* =
•• -P «d
CO
•• o -d
" ?
1
1
i
(0
^ ' n
6
o
0)
CI)
>
1
0)
0)
0
tt)
tt)
>HT5
4H
? ••
c
> "
(0
M
03
Q>
&
ft
&
ft
ft
OH ••
rH
0
0
3
03
H
^
>>
>>
>»
■d «J
0)
■0 »tH
?
•d fa
0
6
•d
+»
p
P
P
P
£ * 0)
o
(0
13 rH
0)
C J
3
P
1
1
•H TJ
p
•H d)
c
•rl W
1
a
a
6
e
g
> f3 O
a)
>
* to
» to *
■d
s
0)
0)
0
0
0
i -h a
03
03
1
13
V
4J
p
P
p
P
43
P
43 -d
43 -d
CO
0)
p
•H
•rl
•H
•H
•H
ft tt) -O
to
■a
ft c
a>
ft c
C
to
^
•H
|
1
1
|
1
Kflfl
o
o3 a)
^
03 V
O
^
p
1
+J
P
P
P
P
MOO)
p
P to
03
P 10
-P
43
S
3
n
S
S
d> 33 to
03
u
tn —
2
d>~
P
C
ft
0
0
0
0
o
1
u
3
&i
03
0)
cu
0
0
d)
P
■coo
P
0
03
P
■d o
w p
<d 0
43
0
U
d>
£
&
&
ft
&
O <H TJ
> -0
o c
J
0 C
M
ft
*
p
p
p
P
p
43
o
C
43 —
E
43 ~
1
P ft
1
0)
P
P
u
■d
d
•d
d
d
a) o
ft
to
a» -d
<u >d
(0
-d
T)
T3
d
■d
6 O
03
^
e c
6 C
>
03
03
os
03
03
<W rH
e
4H 03
>W 03
MH
0) ^
0) ^,
0) —
0)
>
>
>
">
>
■d
■d
■d
•d
p
P
p
p
P
Section 5.5 The Program 99
o CO I
u xi = n
<t» co o
o -o i
S +j o co
CO -H
u «h
CO (0 43 13
45 +J 0}
+J 0 CU
H — -P o *
o g <w *
4-1 CO M
-P O -H +J
<D -H 4-1 0) CO
o "d o> «^ ^ <o 43
C C <0 to — — h ra
O O O ~ <-* I
ViO CVht-csi cC CO
id cu <tf co co co
43 ~ CO h — « •• •O •O" ^ 3
+j ^ _ CO ~ O O co o
O ~ fl- - C C -P E
>. <N B 4J >, g -. •• C ••
•P CO CO -P O CO W>
pi 43 * -d cx-p«j«j = -p
•H V O -H a) -H
oec o^e-o-o tj
o« co co •• h*j « t) B t! ^
to p -P C co co <0
>! O -h a> >JS-HOwcn O
O O I TJ U O I O — — 43
•H 43 CO O -H 43 CO CO >,
puu>C Pn m > co co a> ~ co
= = -H =s-H— ■dT) — .*
£ 4-» <- S-POO — I
OiH-HCU OrH-HTJCC — 6
TJ-HCOTJ -d-HCOC! «— O
CCCO fifl fi IID'D' — H
■H CUC'- -H CO CO CO CO ~ *- *W
S+jcq-^^>+jco— — ^^ 43 i
C I ~ ? fl I — w v
rH-HCUO-— OrH-HCPCHOj CU C!
— iHOCOW— -OrHOCO-HO ^M -H
>njaDnj>C(Oft0 — c a) iw m
OOI O^O-HOI O<0 h ^ -P <U -^ •— I
•Ol-dE -d » I *d g $3 0 ft CO H S >-P
C £3 £3 •• COG £3 G •• H d) +J 0) H •• O O <D
•HCU-H O -rl CO CO -H O 43 <U CO 01 TJ TJ CJ>
s ~ 43 4-1 > c> -o — g <w s <«> na-o? c a • •
>,4JI O ~ <d O >, -P I O ~ — •• O -H -H >
aii^'dft-PM-iCiU'Ofta «os S-p
■d X -P O £3 CO to rH ^-X+JO (30)0 — O £3 — — -»
O — CO -P -H CU C <D — CO .p -H CO O O fc -rl CO « CO ~
£3 CO -rl £ 43 -H 10 O CO -rl £ 43 rH >H flj £ Tj 43 Tj rH 43
^■o x -d «-ii ^■043'0 »-*- <a o co o co co
ficoco TJ co? nJCcoco -O "O -O £3~<U C43 co
O-HII £3— >J(fl l-HII C'-O 4JC3C —con — «J n
nxi+jfi coemn coxiP£3 coen ococo -p«w rH iw
it I CU 01 CO CD g T3 -PIC0CO CO CU (fl (3C0C0 CU CU CU CU I CO
i ai co cu —..p«_-.. cu cu co cu ^-+j^- ^«_-^ t) h ^ tJ-P ^
>SIM -H rH^I^ -rl— O «» •• O <D ••
COrHCOOg-d COrHCOOg <W flTJ J3C0
{3k)coco co-PC tj k) co n copp -h !••> I •• *
i > s •• +j o iii i > d •• 4J o « — cooco o
QJIO>-H(3C0 (0IO>-HC3rH -PCOTJgaj'O
cocogp ^.w.^ cocog+J — «-' — cotJC (OtJ (3
■3 rH •• - — pj H .. k w ,H Q -H £3 O -H
oft>=* <« o a > % M-i coj3>co(3>
g -H -p +> -H g -H +J P -H TJ M
+)^- d)^ 4J— CO— "OTJ "OTJ
(3iH rH J3i-i rH C3CJ3CJ3C
j30 ^ 00 ~ 0COCO0COCO
lUg <4-|g lw tfl CO 4-1 CO CO
<p^ cu_- co^^co— —
100
THE GRAPH EXAMPLE Chapter 5
0) ~ *-
to — "»
o 4> >—
ONI)
.C -rl N
O W -H
= I to
C i
= -H $3
•H Ifl H
o e <o
eu i e
•^ I
M<H ft
O 0) o
•HH+J
pn .. ..
O H o o
•O -rl T) "O
c c n c
•rl -H -H
> -P 5 5
> H -ri T) fl
O rH O C C
c o I to w
■H I >0 -' -'
* g -S X *
0) -- ^! M-i
•d :*p i " '
0 i u «-* «-
a x p o ~
^«- « |J 91 01'-
4) -H O O .C
0> T3 ,C TS ft 0< CO
•O G to 4> X >^ 4>
O -rl I I I IH
a a -p c +j +j M-i
1 | <D 4> CD <U 4>
0) CD 10 4> tQ 10 U
> 3 i n
OH d) o
g id oi to 4i u ^
i > a •• *o tj o
a) i o > o o -o
to <u 6 -P C C fi
fl H ••' -H
O ft > % -o -o s
g -H +J C C _
+J ^ 4) 4) T3
C pH to CO C
g jj >_^ a)
WE «
■d
Section 5.5 The Program 101
5.6 Problem Set #5
Questions
1 . Assure yourself that you understand the graph code.
2. Write a function that finds any nodes with no connections to other nodes and
removes them from the graph. Write another which clears the graph-window
then uses map-over-arcs to display only those nodes which are "nodel" for
some arc.
3. Extend the program to be able to deal with a larger area than will fit on the
window at one time. You'll need some mechanism for moving the graph win-
dow around within the entire space.
4. A. Write a : highlight method for nodes, which puts an already
displayed node in reverse-video. Now modify the functions mouse-
new-arc and mouse-delete-arc to highlight the first node that was
moused while the program is waiting for you to choose the second, and
un-highlight it when you've chosen. Fix up mouse-move-node similarly.
B. Write a new mouse-sensitive operation which has you pick two nodes
and then highlights all the nodes in the shortest path between the two
chosen ones (where "shortest" simply means containing the fewest
nodes — don't worry about ties).
5. {Hard.) Make the arcs mouse-sensitive too, but modify the mechanism so
that the area considered part of the item is not an entire rectangle, because
that would include too much area. Allow specification of parallelograms, or
something like that, so you can tighten the mouse-sensitive area to be just
around the arc.
6. There are a number of problems with the code as it stands. Here's your
chance to improve on the teacher's work.
A. Any operation which requires that some part of the display be erased
currently causes a complete redisplay. This is really unnecessary, and
quite unattractive, especially if there's a lot of stuff on the screen. Fix
the deletion commands to do minimal redisplay.
B. Currently, if two nodes are connected by an arc which cuts across
another node, the line for the arc just runs right through the node. Fix
the : draw- self method for arcs to be smart enough to go around
obstacles.
7. Write a new program, borrowing whatever portions of this one are
102 THE GRAPH EXAMPLE Chapter 5
appropriate, for displaying trees. One important difference should be that
instead of having the user specify the location of each node, your program
will determine their locations. That is, the root will appear at the top with its
inferiors spread out in some tasteful fashion below it.
Section 5.6 Problem Set #5 103
Hints
1. Play.
2. The first function should loop through all the nodes, sending the : delete
message to any which have no arcs, and then refresh the graph window. The
second should clear the graph window, then use map-over-arcs to loop
through all the arcs, sending "nodel" of each arc the : draw-self mes-
sage.
3. Give the graph-window flavor two new instance variables to indicate the x
and y position of the window relative to the graph area. Now the functions
concerned with node positions need to convert between a node's apparent
position and its real position by adjusting for the window position. Four of
graph-window's methods (: draw-circle, : draw-line, rdisplay-
centered-string, : primitive-item) will have to convert from real
node positions to apparent positions, while the : mouse-click method and
the function mouse-move-node will have to convert from apparent to real.
The former four are all inherited from some other flavor, so for them the
easiest thing is probably to define whoppers which adjust the arguments. For
the latter two, on the other hand, we did the relevant definitions ourselves, so
we can change them.
The whoppers converting from real positions to apparent positions should be
prepared for the apparent position being off the screen entirely. The
: draw-circle and : draw-line methods "clip" (they'll even do the right
thing if the circle or line should be partially visible), so you can just pass
along any bogus arguments without worry. The :display-centered-
string method, however, wraps around when given off-screen coordinates,
so in this case you should check the apparent position and possibly skip the
whopper continuation.
(You'll have to make a new instance of the graph-frame, because the new
graph-window defflavor, with two extra instance variables, is incompatible
with the existing graph-window.)
4. A. The : highlight method should draw a filled-in circle in xor mode.
The basic idea for the mouse-!...] functions is to use the : highlight
method once to highlight a node and again to turn it off, but there are
two kinds of complications to watch out for that could leave you with a
node turned on: the : refresh method clears the screen, thus remov-
ing any highlighting that may be present; the new-arc and delete-arc
functions have several failure modes (the parts with the beeps) that
skip portions of the code.
104 THE GRAPH EXAMPLE Chapter 5
B. Add a new type of typeout item to call the function mouse-find-path.
The control structure of mouse-find-path should look roughly like that
of mouse-delete-arc, and you'll probably want an auxiliary recursive
function, say find-path-to. A breadth-first search will be the easiest
way to find the shortest path (because the first path you find will be
the shortest); you can use the mark instance variable of arcs to
prevent looping in the search.
The key issue here, and the reason I call this a hard problem, is compatibil-
ity. We want to use the bulk of the existing mouse-sensitive-item code to
save us the trouble of duplicating its functionality, and we want to modify it
to provide the new features. But at the same time, other programs will be
using the ms-item code and counting on it to behave the way it used to.
My approach is to first replace the hollow-rectangular-blinker that is provided
by the basic-mouse-sensitive-items mixin with a polygonal-blinker, a new
flavor of blinker which can draw itself as any closed polygon (imitating
hollow-rectangular-blinkers as a special case). Then I define a line-item
object, and arrange for the polygonal-blinker to draw itself as a squashed
hexagon around ms items which are of type line-item (and as a rectangle
around all other ms items). A number of basic-mouse-sensitive-items
methods have to be modified, but most of the changes are of one very simple
kind: references to individual ms items are replaced with calls to a macro
which just returns the item if it's the normal type, and extracts the appropri-
ate information out of it if it's a line-item. The only substantive changes are
to the : mouse-moves method, because there's a new way for specifying the
shape of the blinker, and the typeout-mouse-item defun-method, because
there are new rules for determining whether the mouse is over a particular
item.
Now all I have to do is change the : draw- self method for arcs to provide
a line-item as an argument to the : primitive-item message, and define
the "Delete" operation on mouse-sensitive arcs.
(You'll have to make a new graph-frame to put all the changes into effect.)
A. The deletion commands should surgically erase exactly the nodes
and/or arcs being deleted, and leave the rest alone. You can erase the
nodes by drawing a filled-in circle in andca mode, and the arcs by
drawing over them in xor or andca mode.
B. Open problem. I haven't thought of a good way to do this.
Done as "The Tree Example," chapter 7.
Section 5.6
Problem Set #5
105
p
?
n
0
1-\
■0
0
c
1
1
in
•H
1
ft
*
U
CT>
P
*
CO
W
^
^
4H
rH
g
^-~
:s
CO
(V
*
0)
0
CO
rH
03
0
13
A
CO
P
--.
»-.
C
5
O
T3
(0
^-~
«-*
■H
Hi
M
0
CO
^
>
rl
h
l
CO
P
CO
co
CO
m
<a
co
T)
rH
h
«
A
0
CO
4H
CO
■—
-p
1
C
■a
CO
in
rH
o
CO
iH
T3
•— ~
T3
H
C
CO
0
-
*
CO
T3
*
*
»-•
c
*
to
0
»
CO
»
0
CO
n
0
0
u
u
C
TS
CO
73
<c
o
0)
•H
r-\
n
G
T3
a
rl
c
rH
a
•iH
0
•H
rfl
0
0)
0
CO
5
fi
s
CO
rH
■0
a
co
1
1
0
-d
|
0
■~^
k
s-,43
rl
c
CO
c
ft
(0
ft
ro
CO
>
c
0
(0
rH
<0
1
CO
0
M
0)
T3
5h
a
H
rl
^<
g
0
A
Cr>
CO
Cr>
co
(V
4-1
»
*
•H
*
> •«
M
a
■0
T3
T>
0
1
c
CO
a
0
c
c
a
ft
CO
0
0
CO
9
CO
0
— '
<u
H
CO
4H
CO
e
CD
1)
•o
-a
££
0) rl
43 tn O
P P
a> Pi
•0 43 -n
P! P
«D *
co M 0
CO o
3 > rH
o o
>> o
-O P
P c
(0 pi p
43 O P!
P U <0
-0 >
CO
co P P
U -H 43
•H tJ>
3 CO -rl
tr > g
CO o
Hg-
o •
P O rH C
2 P -rl
A X
CO • -H
- co co e
>, &> CO I
P <0 2 &>
•h co o a
rH CO g -H
(0 CO rH
a g CO rH
O 43 O
•H CO P U
P O O
O ft 43 CO
PJ I p I
3 >>-H £
4-1 I > 43
43 co
■O ft rH (0
CO 10 rH rH
■O rl O <4H
co tn u ••
co i cj >
P! P to P
co
co co o *0
43 •• -P P!
P (0
TJ P
rH C P! H
rH <0 CO <0
«5 -H 43
CO C I
CO O CO rH
CO ft > rH
■d I P! O
■H X O rl
> I O U
O 43 to
rl ft CO I
ft «0 rl O
rl O 'H
tj> &1 g CO
Pi I 03
•H P CO 43
> CO 43 ••
O CO >
rH •• 13 P
r-i t-\
O ? P» CO
4H O O P!
T3 » -H
CO Pi X
rl "H +J 'H
e > H g
— P! to
> P
> O O X CO
O -H CO
TJ co co g 3
P! O O I O tu -H
•H ftftCO g^ OH
> I i co i S C ft
i X >,I1H) O id i
43 I I O -H T3 P P
ft4343OC0P!C0-H
irJftftum-HCC
}H * 10 ft 43 > -H -H
0> rl rl I I
Cn tr> > > > co P
M— — PPPrHrH
O >- «- 43 P>
> (0 (0
10 +J IH
rH P CO
Uj CO -O
S * to
i P
•S +• 6
ft co O
It! -H rH 4-1
»H rH -H -
01 10 C —
^ I '
» CO ft
ft I ft
to >, U i0
CO CO P CO g
CO CO I 4* I
rH O g P! P
43 O CO -H G
(0 U P rH O
•H ft-H 43 <4H
u
Rl
CO
CO I
•H I
TJ 43
>0 ft
rl (0
rl
>> C7>
X >,
u co
-^ a
O I
^?
<0 43
rl ft
■O <0
•• U
&>
Bx
T3
Pi I
•H —
I U
43 CO
ft ft
(0 ft
rl O
&>43
I
rl CO
CO p)
ft Pi
ft-H
O P
43 Pi
> O
4H O
Q) ^
T3
106
THE GRAPH EXAMPLE
Chapter 5
a
i
X
to
a
1
1
(0
1
*
r
ft
Ifl
rl
cu
Cn
T3
CO
e —
.-~
0
H
o —
cu
Pi
P CO
N
cu
0 ft
•H N
CO -H
cu
,£3
p 1
£1 — I
1
CO
P
A >>
CO >,
c
1
CT> 1
P 0 i
•H
c
kl
•H A
A <XA
tn-H
0
u a
0> i ft
M
Cn
4-1
10
•H >, «
§
M
— -
p u
P I P
1
c
4H 0>
^
43 &>
i
0
cu
^-^
V
^-»
ft ft
P
i
•H
N
cu
rH 01
P
0 it) 6
4-1
ft
P
•H
N
&> ft
-C
P P 0
CD
0
■rH
CO
•H
tn
tnP
rH
P
CO
1
CO
c >.
•H
-P -P
<-»
0
PI
1
•rl
01
4h ft 0
d) 0 A
^
*-»
>
>
a
■H
Pi —
P. <W
A
CO
CO
P
P
CJ>
•H -^
P O
1
rH P
a
0
s
tH
^~-
tn —
CO CD
V
I
ft
X
►,
r
cu
ifl
—■
U —
— -0
Pi
6 i —
i
P
Pi
e
co
(fl CO
•H
0) ^
>>
>1
^-
1
1
c
0
a o
--»
rH
p —
>>~
—
- —
•H
Ifl
p
ft
i a
t7» —
•H — CO
CN A
A
0
4-1
ft i
C co
> —
CO 0
cu 0 ft
>> ft
ft
X *
+
+
cm
cu
CU
X
o >,
•H 0
P —
•0
«
CO
rH
p i
U ft
CO
ft?A
CN U
(H
co X
X
0
A
•• A
p 1
*~* 0
X c^
Cn
Pi
00
CO
o
0
a
ft
co X
p ft
P X 1
o s
0
a
•H
A
3
id
S ifl
1 1
A £
— I A
T— T—
(N
P w
ft
0.
u
0
n
0 P.
T> A
tn
A ft
►«>»>»
p H
X
s
r
=
-0
CT> "O
CU ft
— -H +J
— ft «J
PI H
•e
C
Pi ••
P. lit
.-. q, #
e m p
T— |
1
A 1
0
rH
•H
■H
CU U
^5 ^3 Cn
cu p tn
X —
~—
~«
cu
T3
■H
*
>
> >
P tj>
P 1 -H
p tn
T3
Pi
C
0
0
ps
■o (u h
•H P
«-~
*->
— H
0
•H
"0
T3
TJ X)
CU P
•H T3
i p A
«-^ CO
CO
>! t>i
C
s
P
Pi
c
Pi Pi
O A
» -rt P
a» m &>
CU o
0
O H
c
CU
■H
CU -H
I 0>
1 CO (M
> CD -H
Pi ft
ft
•H W
4H
»
rH
■H
10
:*
W >
>*-H
D fi D
•H rH P
•H 1
1
rH 2!
CU
rH
0
rH
0
<o p
0 -H tH
P
rH X
X
U W
CJ
CU
■o
Ifl
ft
■d
^ T3
rH
•H ••
■H 1 1
1 1
I
1 CO
Pi
CO
Pi
0
X
Pi
>> Pi
ft M-t
CO tn
e — —
? A A
CU 1
(0
■H
1
T3
cu
0)
co o
Pi 4-1 Pi
•H
tO ft
ft
CO U
— ~
p
4-1
>
Pi
a
1
to
I CO
•H CD
•H iH -H
p e
H <0
<fl
pl CO
CO
rH
cu
•H
? 2
.. CD P
ft CU
■o n
u
0 C3
1
c
CU
CU
^^
A
4-1
CO p
.. p
.. tn tn
a o
rH
•H
CO
■o
>>-P
1
+
+
4-1 CO
•H
•• as
1
0
M
* —
rH -0
s
> r-
OJ
CU
CU
»
Pi
X
P
0
^^
0 CO
<D C U
O cu
0 X
X
* "
CO
,X
(0
<D
p
CO
CO -»
■S&
CO cu cu
CO ft
SS
■o
C 1
1
0
•0 fe
0
1
u
n
cu
■0
CO
A
a
0 A
ft CO
•H 1
•O ~ ft
•H P
•H ~-
Pi J
B
-0
c
CO
CU
X
>\ cu
* X
Pi o
>
*
•H W
P
0
•H
1
1
1
1 Vl
1 1
0> 1 .c
1 P
1 u
* w
*
-d
Pi
X3
p
C
p
P 4-1
.c a
P
co ~ »
A cu
A CU
1
c
— .
1
<D
0)
CU
cu cu
ft ft (M
»-» i
ft ft
ft ft
A TJ
to
0)
^
cu
CU
CO
cu
CO
CO u
(fl <0
a)
co a>
a) ft
<0 ft
ft Pi
C
CO
>
P)
M
P P
rH
P 0 3
u o
U 0
(0 CU
0
^-
p
0
rH
CU
u
tn 0>
.G ft C
Cn .C
&A
U CO
p
e
id
to
CO
cu
01 ?
ft
CU S-H
~ *
— >
&) —
p
&
i
>
0
■d
T3 0
W P
CO
■H P
PI
0)
i
0
>
0
0 T3
P <W
3
ho c;
p cu
M CU
^ P
J3
0
CO
CU
a p
c
Pi Pi
a> cu
rH
0
CU 3
CU 3
•O 0
M
pi
rH
■H
ftrH
ft
V v o
ft a
ft Pi
0 Pi
II
ft
0
ft >
t)
-0 *
ft
ft-H
ft-H
A ~
a
•rl
p
PI
Pi
0 M-t
0 P
0 P
P
P
cu
CU -0
J3 O <0
^ Pi
A Pi
CU T3
§
H
CO
CO Pi
> a)
fi
> 0
> 0
6 Pi
i
— -
_ o>
<W 0
«
4-1 O
>H U
4H tO
<4H
CO
0) w-
0) .-.
0) «-
01 _-
CU
•0
>0
•o
■o
•o
Section 5.6 Problem Set #5 107
tJ> 4H g
a in -h
id en P
43 0) 43
o o en
— O -H
— - SH
— » to A
— 6 O Cn
— o> >d oi -h
H -P — C > 43
O -H — -H
X > S 0> 0)
1 X> O — — H 43
2 (3 fl M-'- p 01 P
rH O CO— — 43 43
Id O -H M x- (N tn > to
iv ? <t3 a* a> -h w
> — 0) -- ••'dt) H (D«)
P -»_- = — POO43t00)
— • — 43 — (3 C 0> *h
10 = >, (N =T3>>t7>e H O O
2 "O P 0> -d X) P 0) -H 01 43
•H 0) 13 X Xl 0) 0) C4J«H+)ldfl •• (DIM
•O" &1 -H OCn &1 -H 0) 43 -H 43 rH
•d J3 06CJ3 a O h 6 d> x« xi o> P oi
w ro o< — o> •• id id o*o>o>-hxii3i3xi to
43 = P 43 43 x» P 43 13 01 0) O (3 P
t0 O M • -H 0) O O 4* -H •• O 01 t0 fi -ri -H
o o o i xj o o> i o «— --
ft •- -H w 0) O — -H 43 0) <u <u -a ID 43
>, Pn 10 > G 0. > XI to 01 0) C too
= -H rO-HO---XlXlO>— 3-H
to SS+jt- SPPGOOto — <d43
O O H J) -ri IV OrH-H-Ofifi^— OS
ft •d-ncio'O xi -h o to >o (3 — ~a>
X CC C O fl fl H C fl Hi ffO1-- -fl -
•h iv u c -h <d o> oi to oi o> — ft — 43
— o> s p 43 to ••— — s p toto^-^^njiu 43<vto
>ih J3-PI — > j3Q)i— ajtowiu
OO rH-H a) O — O rH -H 43 0> (3w (343 0> 0> fc
•K3 U — rH O H 10 H— XI rH O -P tO — -HO H — — W 43 «W
(3 -h > id ft o 3 id* G (dft 3 ft--- rJ v <h o>
•HO O OliwO % O — -H O I M O 0) id +JCPIV+JW
»i -o— 1-d 6 -d— >— i flO Em C o> d> o> n 43 ••
— C fi> C C 01 •• <VC» S B B ih •• fl H« H O H •• tJi
■ri -ho oi -h xl o -h o o> o o> -h — o 43 u 01 -h id
— I > -d — 43 <« O S C > Xl Xl Xl — 43 «H tt) > <wS ft-dSH
■p-d i3 >. p i c o — id a ocj^Pi-doc — •• o 43 x>
430) 0> -H I U Xl ft -P <4H -H J3 -H I Vl CO tJ>ft Xl d> -H
0> rH T3»XPOMC0)t0rH> -'SXPOCCOO — OCI-HTJ
•Hi-I O ^<UP0)-Hll)l3a) ---01-P -HMO OM-H43
rH-H CP 0)-H43>43-Hl0+J OP <V-Hrl*ftrH U * & " Q>
43<w — 43 "d 43 T3 -P — I I 43 H43-d43,da> — — id >
tj>i d»i3tna)0'd 0)>o> ^011310(1)43^ ■d'dnJ
•H ^ O -H -H I I C — M Id -H I H -H I I +J C — O P C C
43ld VlrH43-P(3a>a>aidiHrH 11 H ,Q (J B O 11 g tl O 0) 0) P o
••>h <d43ia)a>43toa>E'd43 P43i<va> toaiid i3tatorJ>H
■d i&i<D(oa>4J»--4J«-'"&i a)di<vtoa)tt)^-P»— ^^^ftm
0) •• > -H r> I H -H -H rH -H ^ I U 43 -H ^
XI 0)43rHil)Oa)e "d 43 0)43rH0)O-PE MH P13
OS (3"idtototoaiPi3 •• ■d"idioto iv+ip -h toid
co i >0-oPoa> i >0-*Poo) — s
^■d Q)a)lO>O-Hflt0 0) 0)<VlO>O-Hj3rH -nul
a io-da>eP43~ — — -d mfl m B+)^ a>
•d-H B O H •• * O ^ O 3 O H •• « W ^- P+J
0> OCft>*= im 13 OCft>%= iw ^0)
43 6 -H -P +J -H g -H P P -H BH
■p*d "dP^- o)^- >o xi-p^- <v <~- <oo)
0)CCC!rH rH CCfirH rH OT3
g tl B V B w O);30>rj w
MH 10 I4H W g 10 IM 111 E ••> •»
•d *d *d
THE GRAPH EXAMPLE Chapter 5
a -
z I U
"2 £ ~
0) G XI ~
O <w O >
0) fl *«
0) to — C
5 3 3 - ~*
O sO) X\ &•*
u~t j5 tj +J to tr —
o — — * £ D,&~
•H ~ — 03 +JO—&> —
jj ^ ^ -Q -H MT>-H~
•ho>— c^ £ 5 ~ ^ ~
"58 Z* u «"*
•> C I 0)0) 01 ^ ^ C T) C
tr -H flj <tf H "A -H +J _ ,G ' ~ -M
G
5
a
a ji i 1 * xi fc " "" 'd "P 5 ?
£ u • • • • « -p * ^ > "2 h tJ '2
R_,00 ft P O H 01 'H O
■rt £ -O T3 K £ -O-H-H03T3OH0)
El* So c2 cfiiflfl
.5 -H I ^ -H 0> 4> 01 O O
S+J 55 e™_ ? c o ? ™~~
"* jj irtfl +J0) S H 'H >, 41 OC"0
n 2 O C C -H .2 O rHO 01 — P-Hfi
.2— it) ~ ~ ft *o -h— i>d-HE<up ^
n.rliiu -^ ^ O-H i n +j tj tn G ft «>
5 ? .2 « +j oioi-> ft " ^<U+J0)-HV4>wO —
ti 0>g>t!)« a a m -n .G&>o>oia>.P~P~ft~
oSh i 3 tr> -P -H^iHttfOttig t»w
i » * w £ <u<i)* c g ^ " IS S " S ?! ^, i
i > 2 •• T5T30 -H O I >3l'8^2^
a .j jj C C I I S "H +■> 4J •"
B T3 -m i- §§A a 2 ^ "9 ii w iS w
fiflH 0101CP-O gCH rH
ih a § tod*- "S016
5 . +J t3
Section 5.6 Problem Set #5 109
u •- a)
o -P 73
>w CO
o o
— o> 03 +j •• o <« 03
~— +J .C -H M W 4-1 .C
---X -h .p ,c p 73 oi -p
.C P >, O -P -H O •- ,C
+J (0 I-H-H73 4-I.C-P-P''
me -p r-\ c <w i p w tn
«-• a -Hamo-paj-Hy-iaj
0) 1^ OX 01 BHIH 01
73 > p -hoj-ojo) o*
0 aim rHg&i^aajojo
C C E ft C 03 fl 3 3 E
1 I gfl+J+J >0J0J0JH
73 03 -P -H -rl fi 4-» 3 3 ,C <H
C 0303 03 (0 03 O* O* -P «
0) P 03 ffl > +J > fc
0) •• ,£(CK73-H.p0)ftp
03 > +J^!03(0>wXX!OO
73 — 03 O (0 C I 03 -P ft fe
O £! P P ft fi 03 .C C
c+j g ~ to m n c+3 i iH-o
— 1(0 — M rHOXiO-O-POC'
0) P ft P 73 « 4-> (0 03 (0 E
O03 CJiflfi -HO) 0) tT> -P 03
p x: oj ceo3 4->sp'P"C--p
(O+j-o-hos vio-pxjo-p-h
• •00 73 ~ •— (0 ► M-l X 73 P M
••CC^ ftl) 1) -fiW-H+J
03 I « 'ri ^ O P 03 fi -P (0 rH X •
-oos+jihatj u-iss « 03 a»i-i
0 P 03 03 I E O-P0373PE.C03fi(C
fi ill C tJlX "fl O 3 03 -H 03 -P ,C O
1 M-PC 033D'3'«-P-P03-H
73-003(0(00(0 S ^ 0) I -H ► .C .p
C C C 4-> ft P 03+J03J-l£!l'4-l<4-(.PC
0303O (OX ^OI^SVOIWO 03
03 O 0) E -P C -P ft ft 73 O t* 73
73 ~ ~ 73 O 73 (0 rH 0)(0AJC-H
— C O P C ft 03O.X0373--EO-H
M 9 I I fX «H 03 I ^hlllfl- OJUflOl
P 03 I I 03 S P -P 03 <U £ X) P -H
(0 — 03 X > C — 03 fi 73 03 ,C -P 3
E 73 -P 03 P G Jl O >iri 1I4J 11+)^
03—co(0C3O' >ofhsx! axojo
03 XX5-HCO,+J03+J (0 03OO(T>O-PP-P
45 +j+j 110*03^0 A 03 > A P C ft I
•P (0(OOSS03P03 > -H 03 (0 -rl O 03 ,fl
(0 ftftPO30)«-'-'O3rH C-H-P 0) >* 73 -P P -P
ft I 75 C C 03 iH (003(0^!03(OCCOlO
I rH P CO03O ,CPP-P03(0OMHft
rH <0(0PPP0373rHO — -P 3 03 (0 MH S 031
(0 -hoooox fi 03 o+jfto-p'Ojxi'O
■ H +J ~ 4H 4H <4H ? 3 73 H03-H CEC
P P O 03prH03>,O0)4J-H
P * ii ft C-- Xfim-o^+Jtniw
(0 ftO ~ 03 4-> 03 03 -rl fi 0} -rl -H —
ft 030 73 (flXX+J-H+JErH
CO H ~ O Jh+J+JP^0103S03
P -HO— 03CMC (0 3 -P 03 03 3
03&1 C 7303 >,73ft-PT-l-HCXJa3
fr C X!lO> O 73 ~ •• ,Q rH C +J 3
P-H -P73C fi O <N OlSX0373»03O<
(073 lOC-H ICO) 3..XOOP0303O-P
4J CO ft03O P73 -H O > -H 03 -P C -H p 03
— -H+J fi 0) -* O 73(OX*4-lCiHOX3
03 <4H I PPO X'-C C+JOS<4-l03Xft03.p
0 ,C I £ OOO ~4->03 •H00+J-HEO3W
+J+JX+J 4h 4H G <— O7303 *4H 10373033 3 M-l
l(0P(0-P ~ •• O 73 l03,C73rHft3OO
£! ft (0 ft 0) ft — CO— .CX-P-rl-Pft 0>!
•P I ft I t7> 0 ^ O C~ -P-P(0O(0EC>i 03
(0rH73PO P P 03 ~ (0 ft 03 ,C -H O -P 03
ftfllX C UH (0 flfl tTH ftCiT3-P >,+J 01 3
1 -H O -H +J — E^OOJ-H -H73 03 03 H
73 -P O <« C^C03C73O3£l+Jp-HO3
CPrH>- 73 > 73 -H rH -H 03 -H <4H 03
•H(0.Q OCW •H03l«CCP,PI^
"Wft— ,C 03 -rl +JCOX(0-HI+J(0
+J ^ ^ fl.H SOO4-I4503E
C73 03 G(0'(0I-P03
CC EM-i H-PO)0)0^4373XIX
y-iio <«-h 03fiS3P--p(0O
03— 03— +J-H0303ftOft03P-H
73 73 rH(0C3ft-P03POX
— — <EO,O,(00373XJ<«»
1 10 THE GRAPH EXAMPLE Chapter 5
-^
«-.
ft
ft
""
^
>
to
0>
0*
G
I
T)
(3
0)
0
Cr
to
13
Oi
*"'
o>
0)
to -»
r) «
Cn
•0
0> 43
<-~
<-^
0 43
> -P
01
C P
oi — id
.-»
•0
0)
1
ft
U X ft
o
3
n
G U I
- s
c
0)
0)
— id — £
~ 0)
3
43
0)
e -v o»
— > P
43 ~
cr
P
T3
•O rl C
e -h
•p ~
to
0
O
$3 — id
0)
Id '
*-»
o
J3 ^
v x e b
P to
ft
e
u
0 U 01
•H fi
1 ~
0>
«-»
id
O
> P
4H id M P
0
■o e
p
P
rl
0> Oi
1 S rl -h
to u
c 0>
•H
X
«
C &>43 •• id
a g
•H P
^
*
01
43 0)
u
+» g *d
o —
«4-l -H
«»s
T3
n
c
P TJ
T3
to id
id o i -0
o
1
<u
G
Id
1
id 0
(3
a p
ft u P id
g u
o> -o
3
— O
B
p
ft C
0
0
id oi ••
g
- p
to c
0)
<-» O
Oi
to
U 01
B to
01
ft
3 0
3
Oi 0>
01
Cn
U TJ
•-^
— -0
O TJ •• 01
•p
U 1
o o
cr "O to
0
id C
0
rl C 0
•H
■p -p
6 0)
o ~
0)
O 0>
II
II (3
MH 01 O 0)
>
ft s
«_»
to
G
3
01
— to
1 to U 3
I 0
J3 —
a*
- o
cr
n
< —
0)
43 i
c — id cr
p
•H
o
— +J
0)
II
T3
P Oi
rl
e
3 0-
■P
o
(3
' 1
p
3
a
0
ftC
3 cr tj "O
a>
O P
X
0) P
03
„ A
<D
cr
0) -H
C
P Oi C (3
■P
01
0>
C 1
P
b p
tP
X)
1
i cr oi — oi oi
■rl
cr to
c
•H 43
to
0> 0)
rl
■O TJ
o o
S
> o>
rl 01 tQ
1
p «-
1
rH P
a
p a
id
(3
(3
13 U
0)
Oi —
^- to «^ ^
— »
■0
0*
•p
Itf
■H
•H 1
p
3
<D
id
(3
{3
(0
<-» rH
•0
to U
a>
Cr ft
1
1 T3
o
W
43
(3
o oi o
rH -H
n)
>— -p
Cx>
C 1
01 T3 C
4H
~_-
P n
rl
rl 01
•O rH -0
•H a
-— .
ft
•H -O
M -0 -H
0
1
•H O
0
0 43
G
a
n
M 1
> C
1
0) 4-1
p
43
ii
43
> »w
4-1
4H »
0
u
a>
p
-p p
o>
0 -H
.. —
1
■P
P
o> n P
3
a
ft 0
0
rH <W
43
id 43
<d
ft
d P ft
a>
I 0
0>
»■"«
rH «-'
0> 43
P
ftp
ft
O
V ft 1
0
c
g
0
»-»
O
0)
n p
m
■d
0
3 i -P
cr
■rl
■H -0
cr
rl
<W 43
d
0> 10
ftT3
ft
<D
rH
trc s
o
p
■P
0)
3 ft
0)
rH
■H O ~
rH
cr as
ft
0) id
3
cr —
■a
e
u
•H
n ~ «--*
T>
rH
P rH
■o
1
43 ft
cr
(3
<d
0 43
0
O ~ ~
0
fJ
0> ft
0
c
P «-
•o w
•H
C
4-1
> TJ
>
43
J3
to u
43
•H
~_^
C P
4H
Id
p
p
01
0) o>
ft
1-1
o>
B
0)
ft
O P
p
tO rH
C
0
«w
s
4-1
o>
a
a
id 0)
0)
^^ ^-»
3
0
<H
4H
•H
p
4-1
rH rH 43
rH
4H
r-t
a>
01
■H
0)
ft — P
<U
TJ
•0
•o
01 -rl
TJ
w
*■*
w
U IS
"■*
Section 5.6
Problem Set #5
<T3
Xi
•O
U
^«,
Cn
o
o
«-»
o
^~
C
0
(0
x;
W
•H
u
T3
s
CO
>
o
U
8
d)
p
1
i
•H
6
o
3
co
UH
w
H
rH
fe
0)
0
CO
2
rH
•d
p
i
03
xs
■H
0
B
>«
0
0)
CO
c
0»
CU
P
P
X
■p
A
•H
CJ
CJ
3
a
•H
Q
rH
(fl
«3
O
a
J
■H
U •
H
01
ft
o
■H
UH
■H
■P >,
rH
^
CO
1
o
CO
CT> 0)
p
P
B
1
•H
M
C3 P
0> >,
•H
s
>
c
■H flj
0> 1
id
CD
3
•H
CU -H
X3 P
ft
XI
rH
—
UH
rH
X> T3
(0 CQ
p
T3
•H
X3
0>
P
>>
CU
C
CO
^~
Si
rH 'H
e
<TJ
p
co
>^
G
>l
•H UH
X*
n
X!
•H
p
■H
X
0)
ft
rH
C5 -H
CJ
X
UH
CD
0
ft
m
■H
0
o
10 G
P 1 ~
<-»
O
p
■p
ft UH
-^
•H O
O P ~
•H
Uh
1
p
X CO ~
-^
P
CO
3
■o
0
UH
0
_^
c
H
H
p >J
•H O
1 P P
">
•H
CU
0
■p
1
X
a
2
2
03
rH UH 0)
+J
rH
P
A
0)
p
T3 a
03 X!
•P
•rl
CO
X
cu
cu
a
CO
^ >» CO
0)
rH
■H
•H
1
03 P
p
>> 1
0)
s
rH
UH
3
S
•H
C
> rH
Cn
-_^
O
CO
o
•>
•H
X 0) -H
fl
to
^»
CJ
J
P
J
u c
0
P CJ
a
CO
■H
CO
J
•H
J
0> P
rH
ft
:* ft
u
rH
•P
CJ
o
03
o
AS CJ
^-~
•H
1 u
(0
•H
p
CJ
—
0
Cu
ft
fe
C P
^
c
p
> x o
a
C
o
•H
0
^
u
CO
UH
CO
•H
rH O
H
03
O
o> i x
p > i
TJ
^»
p
ft
P
0
p
XI -P
J
S
~--
ft 0) C3
c
CD
C3
p
•H
1
■H
CQ
2
X
►<
P rH
■H
N
o
Eh
X
o
n3
p
03
0> P
■H
II
CO
X ft 03
UH
•H
W
(D
ft
CO
ft
co a
rH
c
CJ
p
1
CO
p
CO
rH
•H
C3 01
<-»
XI
---
CD
o>
CJ
> 0)
tP
*~.
cu
u
rH
UH
rH
UH
O P
>n
*-»
>.XJ
■H
0) CJ
*
co
(0
0
0
0
^-~
0
6 P
E-i
'-.
P
p
0
P -H
a
01
P
g
UH
o
CO
fc
0
H
P
P
ft
ftrH
PJ
rH
ID
^~
P
p
J
P
0) ft PI
^^
0)
CO
X
>,
w
XI
*
CO
g
ft
>H
CO
C
w
CO
xi e
H
&4
X
p
1
1
P
01 S
a)
tt
05
CJ
^
0
E-
■H
•H
CO
■H
P -H
CQ
J
a
■H
p
P TJ
CJ 03
Cn
2
•H
•H
p
H
0
H
rH
a
rH
H
w
w
•H
UH
co
CO
U
•H P
ifl
H
P
rH
0)
PI
r-\
J
«
CO CO
CO
CO
CO
rH
P
p
rH -O
>J
J
03
£1
M
CO
-^
Ei
H
CO
w
CO
•H -H
H
i
XI
■H
•H
o
CQ
>
1
a
CQ
P
rH
I
P
>
1
UH
UH
CJ
> P
(0
rH
•H
rH
CO
>H
H
C
id
fi
CO P
Cu
rH
X
■H
03 0)
ft
rH
cu
10
rH
Jr, <0
P
9
CO
•H
3
H
•H
•H -H
o
1
H
03
II
II
P 0)
05
o
c
X)
rH CJ
•H
H
a
cr pi
a
X5
w
Q
J
C
P
•O X!
—'
CJ
a
0
P 0
n)
w
>
0)
PQ
P C
2
J
CQ
O
CO
X
>1
>.
1 CO
0
03
Cn
P
0) tP
ft
£
1
0>
O
Cn
p
1
■p —
p
Cn
— «
P
>.
0)
ft *
i
J
o
2
g
UH ^)
>i
■H
>
>
0)
a)
>,
(0 W
CO
r-\
■P
0 rH
UH
c<
J
Eh
p
W
H P
Q
rH
UH
cu
CU
0) >,
rH
rH
P *
C
a
^—
<H
aa
c
H
W
0
CU
w
9
O
w*
p
p
X
J3 rH
1
a
cj a
•H
o
nj
*
2
CO
c
o
CO
.» ••>
ft
ft
ft
— '
CO rH
p
•H H
p
2
■C
<— 03
<D
O J
ID
o
T5
p
CO
H
P
p
p
p
CJ
M
*
ft CQ
r-i
T3
0)
0) Q
•H
J
X)
•H
0
0
0
O -rt
•H
o
XI
O
n
rH
* o
r— |
CQ
Q
0
S
UH
UH
UH
T3 UH
ft
>
05
.£
cu
0)
CJ X
1
2
.£
S
<
p
P
3
T3
•H H
tr
K O
p
ft
0
J
p
(1)
rH
O
rH W
p
Eh
U
01
8
U
fc
a;
e
(0
B A
0)
H
e
~^
co
UH
d)
>
Pu
- w
CO
3
UH
01
rH
>
D
-d
»•>
- Q
■d
P
' —
^^
••
•- *-•
— '
1 12 THE GRAPH EXAMPLE Chapter 5
^
?
c
»-»
j
•rl
CO
4-1
H
X
a
0)
P
1
2
0)
c
i
a
H
•H
Eh
0)
H
e
•p
ij
p
2
s
■H
H
0
o
*
0)
H
<4H
O
fa
H
w
*-»
CO
-o
X
4-1
H
0)
Eh
a>
— >
to
CO
a
P
1
a os * ~
3
W
1
to
z
H
a
0) o ~
to
O
J
0)
e z os ~
•H
%
p
O W fa
§
>H
•H
a h * j
w
i
*
rJ — z w
1 H (0
Oh
E
Eh
H
Eh
PS
o
a
ft— J
2
CO
0)
■p
o e m «
ft H 1 o
w
04
O
B
•H
Z rH H
0) H rtj PS
.c •• c w
CO
Eh
fa
u
a
o
fa
+
0)
P 0 04
S
J
^»
■p
ps c* d
—*
w
•H
1
•o w >> CO
I
Eh
2
W
CO
X
01
«J fa 0 »
Eh
Eh
PS
c
2
H
o
■H
10 •• » p
CO
*-*
rH
6 Z
o
2
^-~
B
0)
0) to fa w
fa
CO
o
fa
B
P 2 J 2
2
fa
J
u
rt
•H W W I
P
w
w
i
6
0)
ype
E-IT
ER S
TARY
0
Eh
H
=tt
CO
1
o
•p
4H
j
--~
PS
a
-—
•H
rH
B
j
2
o
0
4-1
I
P > K Z
H Z W
0>
<
w
CO
o
d)
0)
CO
H
O
Eh
B
M
c
=1 En H 2
p
Eh
H
1
•H
C H J O
•H
H
o
■o
a
rH
0) to ffl 2
CO
fa
5
0)
0)
x
a S5 1 »
to
z
1
<D
X
e
p
w w
<fl
w
OS
P
m
•H
4-1
<D to ^ S
CO
fa
■H
2
a
-^
0)
P W 2 Q
p
X
1
u
-^
P
0)
Eh W
w
P
Eh
a
4-1
1
CO — Z
£
CO CO
J
U
H
s
0)
0)
a
P D H
P
H D —
(0
01
p
U
0)
0 O PS £
•H
•J O —
p
w -» —
+J
■H
1
•p
4-1 2 W 1
o) <:
to
p
04 Eh —
•H
|
e
•H
1 « W
I I 1
o
X
>H CO —
1
P
0)
P. U Z ^
W U PS
sg
<u
E<HO,
0)
0
p
0) H H <jj
a
04 H O
■.?§
c
03
•H
0.
.* co iJ 2
0)
>H CO CO
1
•H
rH
c
P
P
*"'
0)
ft
■h m i
p
•H
^alg
Eh
y
CO 2 t^
H W ^
«-»
0
X
>,
ri-2g
2 — U
2
H
JtHS
•H
0)
rH
•p
X) HZ
P
w
PS
— H 1
P
p
C
■ —
Q Eh W
0
Eh Q X
O
fa
W
O
«J
0
0
(0 O H 2
0
HO —
fa
K CO
3
0) P
u
4H
«
K ~
CO CD
u
3 C
o
0)
■H
0) Eh O
* W Eh
0>
0 Eh
fa
D O
P B
•H 0»
<T5
u
«_»
ft P W Eh
H
fa 2
(0 0)
•0 -H
e
c
*
«J 2 W
&
C 2 W
w
4-1 P
« >H
4-1
0
2 fa CO
•H fa J
0) -H
u o
<D
•• H —
•- w ~
■o
73
- Q
•-
— Q
Section 5.6
Problem Set #5
13
P
—»
> Eh
,«
0 fa
&»
^-»
■d w
— -H
>,
PI J
-. 4»
-0
■H 1
e x;
* fa
tt) X)
— e
Q
■p
e o
C7> H
■H *
O P
PI CO
as
_»
p -p
•H Z
w
c -» —
P — 0
P H
Eh
o — ~ ~
2&*
— CO 1
H
•H ,C 4J -»
— -H Eh —
^->
-4J+J^^
« -p i
— X fa —
g
<-» Ifl T) t* P
-^ 0) W —
>H *-
g P -H -H T3
. •>
e i » fa
«-">
3
tt) Pi £ tt) -H
O 4) CO O
X Eh
P tt) X) A »
^. .
■P P -^ Eh
EH
CO
■ri -H Xt XI
X P
•P ft 1
K
JH
£ H
P 4S
■O X! -P
O Eh W O
£3
CO O P -» *■»
tn«w
X) tt) fa Q
H
X
3 1 -0 X! X!
+J -H tt)
ft
'6MH
fa
H 1
1 W
_^
•h e -h +j p
X3 4) ? &> C7>
4H P rH
0) ► «
0
p
O J co
• co Z
,-»
g£
u
(0 +J Xt C C
rH —* w
•
1 H
<"^
co
tt)
W -H tt) 4)
•P X! >- I
x — —
I
X
1 1 * H H
/
*u V E-i
S-rH
Eh I
c
e o) —
— —ft
tt) -H fa fa
CO W
.-»
•H
tt) Pi CO CO
- >, O P
rH » W W
H Eh Z
g
-«=
rH
p -H + 3 pi
S3
— TJ P^X!
* as is
i
2 Eh
A
e
•H iH —' -H -H
O
—
0i
— PI Z CO
PS fa fa
fa fa
w
W H
£ Eh -*
1
01
1 — T3 T3
■H
e
— — ft
•H
tt) H —
CO
CO
W H —
r-f CO
p
tt> P (0 nj
P
0
— e o •
P
— ft J
fa « «
o
g
.-^
Eh 6
Ifl P
•H
S3 S3 P U U
<fl
p
— OP
»
6 ft n fa
fa z z
o
W £
,—
H £ 0)
S3 S3
|
■h o cr
P
p
>*** c
w
OK I o
« H H
s
CO w
E-i
g
O P
O -H
0)
rH -H CO * *
S3
0
TJ P + X
o — -o
V A T. fr
Z J J
H> Eh
H
Eh Eh -H
sa
S3
^ jj ^, ^, »_,
4)
XI
-—
P fa
m cq m
O H
Eh
SB Eh
•H
<d
•H
1 XI -
ft
O rH En 1
J i i
10
£
E-t
H
O o e
^~.
rH 1
rH
co P X! \ \
P
ft
^ ► +J
0 ^3 rH H —
m £ £
i w u
s
.. pq
fa
h m id
fa
ft 0)
3 C P s. \
0
0
• A
■P
- -H
0 w
» fa
fa
fa
fa i p
Eh
B
•H tt) tx> — ^
p
rS • C7>
»
? 55
£ Eh Eh
P Eh
>H
J
O
1 £ -H
fa
» CO
e
•O -h P!
cr
■O V ■*
. — o
W H H
H
fa Eh
1
Eh
2 fa I
fa
0)
« P tt) X >,
4)
4-1
P X! P
fa
** ^ ^
(0 1
J 1
s
w eh e
J
£
P
P
P O rH TJ TJ
■—
ft
O" 4h Cn
P 4) fa
HEh|h
4-> W
W £
u
i
Eh H 0)
8
0) p
•H
Ifl
p 4) -H 1
P
45 ft O
C >
CO fa
Eh
H 1 P
Eh
* 0)
»_*
S3
*
tt) rH P «-
4-1
tn >,CO
W H H
•H H
—
Z i
H
Eh
1 Eh -H
s§p
X
o
H
Eh
O
m
•rl C
ft
tt) *
ftp
4)
9
CO
(Q . ► ^
41
N J J
H t-i H
as
CO
2
1
Eh
H
Eh*
W ^ " *""
-
CO fa fa
CO
fa Eh
D Eh O fa S
33
fa
XI rH
^ tt)
—*
'
—■
«— rH 1
1 H H
co w
fa
CO O O
a
fa fa 0 o
1
1 XI
P
rH
O Eh
Eh CO CO
o
— o
fa O
fa >H 4)
Jm Eh ft
H
1
^
s i
^
-^
*
fa
W H H
3 CO
CO
fa
fa
w
fa-
-^
tt) s
CO CO
CO > >
ii
gg
>*
Eh
fa
Eh — >,SC
as
Eh
as
p tt>
•H +J
4-1
•H
•H 1
fa
VC ^> b*
CO
u
fr> tr>
Eh
2 «
fr> 0
•H
tt) fa
www
V ID
1
H -~
ElSa
te
Q
H
ft
CO it
U, CO CO
J3 O
fa
Eh
8
H
w
tt) Ti-
r4 Z
Z 1 1
P £
w
a O
b* CO
fa CO
fa
fa
O Eh tt)
IS
as
ft S3
tt) H
(BLI
NKEF
NKEF
1
6 U
fa
8
H O -P
fa fa -H
H
3
pq
m
>, tt)
P CO
•« fa
0) H
H
CO <
n
o
P CO
J
_
Eh
M M
•riS
PQ
1
Q
Eh
fa
CO
4H
•H
fa fa
>1~
Pi
W
%
fa
*■"'
*"'
Eh
flj Q
CO
^
M%
w
*■""
C Eh
CO
p
•H W
D z
rH £
o
O
m fa
2
o
14 THE GRAPH EXAMPLE Chapter 5
,-»
o —
■p £
O ft
•H W
^^
» J
Eh
^-»
o w
V< H
i to
id
S|
i
2 £
w w
*s
Eh
£ Eh
1 H
c —
W
g~
<D }H Eh
S§
B (0
o w
X H
Eh Eh
W ft
% 3
1 H
£ 1
££
a o i
3 Eh ~ W
£§
Eh I
Eh — 0.
H Eh
01 D JH Jh
an Eh
H
Eh
ft 1
= H
O — X I
SB
0)
to ft
o o
8§
•P « W ~ Eh
— w
«UH-H
ft
^ Q
D>HH«
W X
U U
•rl J 1 2 W
M O W W ft
1 1 > Eh >i
Eh
Eh Eh
E3 D
3 W
ft
U
0) 2
4) W H H Eh
2
w a
(0 (0 Eh 1
H
X 0)
£ W
0 O H w s
OOIOP<H
1
W P
b £
Q
1 -H
B £ SS £ Eh
•• W Eh H
SB
2
O
to 1
1
o o
H
a to i s o
<l) £ w w to
W
w (d
Eh
U
ft Jh
O
P W 10 Eh 10
ft
X +J
§
•H Eh D H <
o
Eh X
H O 1 —
ft
.. 0)
ft
<7> 1 S Eh
I
a w •• & w
2
B
•H > * O A
gT
Eh
T—
.X H W {m
ft
to
ft
G Eh ft ft Eh
ST
J
H
n
•H H iJ >H 1
ft
J
to
rH tO W Eh S
£) 2 tO -' W
ft W
to
W
to
to
to
w
W Eh O
p
D O
01 to Q W H
Eh O
2
O
o
A l 2 ft
U £
w
£
ft
P w w x o w
to
7
ft
to tO Eh Eh
•J %
Eh
%
m D- i W
W —
•P O S3 to
to
o s £ w —
0> 1 W Eh —
rH U Eh H
0) H H «- Q
to to «- Z
■P ft Eh Eh O
<W >- W W —
0) to J
HQ--
I O —
0) K
to Eh Q
3 W 2
O SS o
S ft o
Section 5.6
Problem Set #5
15
w
B
0)
^^
e
cu
■p
-—
1
<-»
P
•H
B
E
cu
•H
B
cu
p
,_,
P
E
0
•H
S
o*
— -
^
■H
0 /
p
<N
-—
/
+»
p
ft
>J
*-%
ft -P
0
0
B
0
0
c
43
p
CN
3
0)
c
p
J2
0
1
X
5
P
0
1
1
•H
E
B
—
■H
•H
E
E
•p
0)
<D
1
X
S
<D
-p
cu
P
01
•P
(0
p
p
•H
P
•H
p
X
£
0
c
•p
■H
•H
c
1
1
t^.
■P
•rl
G
1
I
0)
P
P
g
CO
g
•H
<-{
a>
P
P
■H
ES
13
en
1 —
^
•H
0
0
M
0
0
^x
S
0, ~
B
c
M
0
0
0
0)
cu
w
p: e
cu
0
O
cu
<D
ft
ft
Eh
•H CO
-p
•H
a
e
cy
fh
K
H
rH -P
•H
p
0*
K
CU
p
p
0)
I
» -H
1
id
0)
p
p
•—
^
^
a
t
—
— e
P.
p
""'
w
*■*
t»
•H
rH
H
Eh
H
B CO
■rl
0)
'4-1
•H
Eh
0) -P
iH
•H
■H
~^
(0
H
+J -H
u
«^
10
•H 1
CO
0
■0
2
E
d
^»
B
C
W
Oh
6 co
•H
E
e
0)
10
(0
CO -P
■o
0)
a>
p
1
— +J -H
«
p
-p
■H
^^
w
— ^ — > -H 1
h
•H
•rl
>.
o en
■p D
as
w
Eh
M ITEM
ITEM)
ITEM)
-item-
ypeout
i
B
1
0)
■p
P
ft
O
«-»
<D
c
4H
Cn
X*
^~
tn 2
^
-—
P
■H
a>
•H
ft —
--»
C 1
■H
rH
i-t
P
— CM
•H O
p
CO
i
w
Oh
>H
Eh
>.
■P H
^
i
g
TEM-BOTTO
[TEM-LEFT
TEM-RIGHT
( typeout
e-item (t
CU
a
0)
s
a>
E
0)
-P
^ ^
•H <
.— ~
o
Eh
•H
0
+J
P
■H X
^ c?
o m
to
?
H
■H
■H
•H
0
ft >^
a
s
^
-P
1
I
I
-— .
ft<"
s
w
9
IT3
o
-p
•P
P
—
>.
<N CN
<*H W
Eh
Q
■<■»
CO
-P
r>
3
^«
ifl
X >^
•H Eh
H
w
u
Eh
3
a
0
0
CO
H
Oh
X
•H
0)
p
c
•H
0
ft
a>
<u
n
^~
C X
* *
0) 1
05
&
Q
h v h a c
T3
•H
ft
ft
•H
0)
CO W
o
2
i
s
w
Eh
H
TYPEOUT -
[ TYPEOUT -
TYPEOUT -
ot ( type
et* ((li
*
U
>> >.
>!
-d
0) >,
3 CO
CJ
pj
W
^
M
0
•p
-p
<ti
--^
> ft
t—
T—
— ft —
0 D
§
Eh
o
-—
>H
X —
p
s
■p
>,
>>CN
E O
H
s
J
w
0) X
>> CO X
2
Eh
H
s
W
Eh
H
Eh
a ft >,
■p
0) 1
CO
O
i
W
CO
2
H
ft
>.
X <N
£ Eh
H
>H
Eh
Q
v/
0)
ft ft X
+J&J
1
W
W
O 0)
4H
4-1
E
O
1
§
^—
CO
Oh
s
- - CH
s
c c
U
CJ
* 0) *
B W
s
CO
Oh
*
X * X ""
d -rl
0)
CU
^ +J ^
0) Oh
w
Eh
S
*-'
Eh
Eh
+J rH
■d T3
•P X
Eh
H
w
Oh
W
A\ rl
W
CO 1
1 * +
•H Eh
H
Eh
H
O
5
V/
Oh
A\
v A v 0
0,
•H 0
•0 -P
^,
^,
^ -" *^
q) a
CO
-^
^
o
1
t—
T—
ft\
A o
g
s
J
Eh
Q
Q
0) +J
X
X
E \
+J x
w
J
X
W
|
I
X3 C
0) ^
Eh
Eh
Eh
§
CO
CO
■P -H
X
CN
■p
S^
H
H
0
ft X
— +J
T3 ft
-- p
3 1
~^
v^
h
C
4H
iw
cr
•P z
H
•H C
o
u
+J CO
0) 5
8
<« d
CU
cu
0) ^
OS fe
m
•O -0
rH
••> w
.► 0)
•- Q
.-TJ
116
THE GRAPH EXAMPLE
Chapter 5
11
IS
U W
o> <
p
o p
<i) 2
rH W
fl w 0>
•p m &
•rtODJ
SO 3 W 0, +J w
K rH -
x> i j a --
p 0) I <
ozhehHO
S OJ W 2 W
0> I N •• H W
Pi En -H * «- ~
•rl P P
43 O H
p W O)
04 >
4J 5m -d
(V c
rH ZP
01 P
CO fa ••
«- CM
0) 0)
01 0)
*- cn
0> 0)
"8S
a ps
0) 0)
T3 TJ
H M
cn e
r- CN >,-H
Oi 0) M
^ -^ -o tj cn a
> CN O O X ••
o >, Pi Pi
•o «- to e
a cn -o •o >> o> oi
•H X Pi Pi rj +J
S a> a> r- -o -h
— r- (0 CO X Pi I <w
>,^ _ rfl 0) rH
CN
CN
CN
— CN
a*
(0 r-
3 X
rH
a x
-1
§*
rH X 0)
0) «- TJ
CO O
I -O Pi
5 Pi I
Id'ri m
MAO
TJ i I
•• 01 CO
rj ft)
OH t)i
H nJ «0
«j > 0)
^ i i
0> T)
•O rH Pi
O ft-H
J3 -rl <M
+J -P «-'
ID rH
6 a
p >*
0) A Pi oi <d
Pi i -h to to P Pi
■HIHH 0 a -H
rH >H I S -H 0) 6
I I 0) 0» T3 -H ^
J TJ * P <fl H
* fi < 'H H O -
H 0) 6 CN
•O to •• > > > X
.. .. > +j +j +j
o o
•O -O O Pi
Pi Pi U -H
•H -H (0 6
> * •• w
Pi Pi
0) 0*
Q£
01 o
&s
p
I CO
B -H
0) 43
P P
•H
I 0>
II
U 0)
Section 5.6 Problem Set #5 117
o
u
«}
to ->
"d **
.C to
■p d)
i -» n
■p -p a>
0) (U M
rH i-H ••
■O -O >
I •• O
a» -o
woe
9 * *
■a -o
C C fi
3 0) a»
Chapter 6
STREAMS AND FILES
6.1 Streams
Since the mechanics of interacting with different kinds of peripheral devices vary
widely, and are often quite messy, it is desirable to shield programmers from having
to know the details of such operations. This shielding is accomplished by routing
all input and output operations through streams. A stream is a message-receiving
object (some use the flavor system and some do not). There are different kinds of
streams for the different kinds of peripherals. All streams accept generic com-
mands to perform some operation, and take care themselves of the details of per-
forming that operation on their particular sort of device. This way, knowledge
about how to perform I/O operations is segregated into the streams themselves,
freeing programs (and programmers) from the need to understand the details of
these operations. All a program needs to know is how to deal with streams; the
streams know how to deal with everything else.
6.1.1 General Purpose Stream Operations (3.2, Volume 5)
Some streams only handle input; some only handle output; some do both. There is
a small set of basic operations that all output streams are required to handle.
120 STREAMS AND FILES Chapter 6
Similarly, there is another set that all input streams are required to handle. Addi-
tionally, there is a somewhat larger set that all streams are guaranteed to accept,
even though they themselves may not handle them. This bit of magic works
through the default handler. Whenever a stream receives a message it has no
handler for, it passes the message on to the default handler. The default handler
then tries to use some combination of messages the stream does handle to produce
the desired effect. For instance, the :tyo operation is required of all output streams.
It outputs a single character. The :string-out operation, which outputs a string of
characters, is in the set that is guaranteed via the default handler. Some streams
handle this operation directly; for the ones that don't and pass it on to the default
handler, it achieves the same effect (albeit more slowly) by repeatedly sending the
stream the :tyo message.
Among the messages all streams handle is :which-operations. The list returned
includes only those messages handled directly by the stream. Messages handled by
the default-handler on behalf of the stream will not appear in the list.
Here are some of the more commonly used messages which are accepted by all
streams of the appropriate type (input or output), possibly via the default handler.
:tyo char
The stream will output the character char. For example, if s is bound to a
stream, then (send s :tyo #\B) outputs a "B" on the stream. (Recall
that "#\" is a reader macro that expands into the fixnum representation of
the character code for the given character.)
:tyi &optional eof
The stream will input one character and return it (as a fixnum). On an
interactive device this is likely to mean first waiting for input to become
available. The optional eof argument tells the stream what to do if it gets to
the end of the file (however end-of-file is defined for that kind of stream) . If
the argument is not provided, or is nil, the stream will return nil at the end
of file. Otherwise it will signal an error, and print out the argument as the
error message. (This is not the same as the eof optional argument to read,
tyi, and related functions.)
:untyi char
The stream will remember the character char, and the next time an input
character is requested (presumably via :tyi), the stream will return char.
Some restrictions: you are only allowed to runtyi one character before doing
a :tyi, and you aren't allowed to :untyi a different character than the last
character you read from the stream. Some streams implement :untyi by
Section 6.1 Streams 121
saving the character, while others back up the pointer into a buffer.
characters
Returns t if the stream is a character stream, nil if it is a binary stream.
direction
Returns one of the keyword symbols : input, : output, or : bidirec-
tional.
:listen
This is a test to see whether there is any input waiting to be read. On an
interactive device, :listen returns non-nil if there are any input characters
immediately available, or nil if there is no immediately available input. On
a non-interactive device, the operation always returns non-nil except at end-
of-file.
:tyipeek &optional eof
Returns the next character that is about to be read (without removing it
from the input buffer), or nil if the stream is at end-of-file. The eof argu-
ment is interpreted as for :tyi. :tyipeek is defined to have the same effect as
a :tyi followed by an runty i (if end-of-file was not reached), meaning that
you may not read some character, do a :tyipeek to look at the next charac-
ter, and then :untyi the original character.
:string-out string &optional start end
The characters of the string are successively output to the stream. Many
streams can perform this operation much more efficiently than the equivalent
sequence of :tyo operations. If start and end are not supplied, the whole
string is output. Otherwise a substring is output; start is the index of the
first character to be output (defaulting to 0), and end is one greater than the
index of the last character to be output (defaulting to the length of the
string) .
:string-in eof-option string &optional start end
The stream inputs a number of characters, reading them into string, string
may be any kind of array, not necessarily a string, which can be very handy
for reading from binary files, start and end specify the substring to use,
defaulting to the entire string, eof specifies what to do if end-of-file is
encountered before reading the intended number of characters. If nil,
:string-in returns normally and sets the fill-pointer of string (if it has one) to
point just beyond the last character read. If non-nil, the condition sys:end-
of-file is signaled, with the value of eof&s the report string.
122 STREAMS AND FILES Chapter 6
:clear-input
The stream clears any buffered input, i.e., input sitting in its buffer, waiting
to be read.
rclear-output
The stream clears any buffered output.
:force-output
This is for output streams to buffered asynchronous devices, such as the
Chaosnet. Any buffered output is sent to the device, rforce-output returns
immediately, without waiting for the output to be completed. For that, use
rfinish. If a stream supports :force-output, then usage of :tyo, :string-out,
and the like, may have no visible effect until a :force-output is done.
: finish
The stream does a rforce-output then waits for the output to complete before
returning.
:close &optional mode
The stream is "closed" and no further operations should be performed on it.
If mode is : abort, and the stream is outputting to a file, and it has not
been closed already, the stream's newly-created file will be deleted, as
though it had never been opened.
6.1.2 Special Purpose Operations (3.3, Volume 5)
There are a wide variety of operations particular to streams for one or another of
the peripherals (files, Chaosnet, windows, etc). Most of these are not handled by
the default handler, and so would result in an error if sent to a stream which does
not itself handle them. The bulk of the special-purpose operations are documented
along with the type of device they're intended to be used with. Here are a few of
the more commonly-used of these operations.
:tyi-no-hang &optional eof
Just like :tyi except that if it would be necessary to wait in order to get the
character, returns nil instead.
:read-cursorpos &optional (units : pixel)
This operation is supported by windows. It returns two values: the current
x and y coordinates of the cursor. The optional argument is a symbol indi-
cating in what units x and y should be expressed; the symbols : pixel and
: character are understood.
Section 6.1 Streams 123
:set-cursorpos x y &optional (units : pixel)
This operation is supported by the same streams that support :read-
cursorpos. It sets the position of the cursor, x and y are like the values of
:read-cursorpos and units is the same as the units argument to tread-
cursorpos.
:clear-window ( : clear-screen in older code)
Erases the screen area on which this stream displays.
tread-pointer
This operation, and the next, are supported by streams to random-access
devices, principally files. :read-pointer returns the current position within the
file, expressed in either 8-bit or 16-bit bytes, depending on the type of the
stream.
:set- pointer new -pointer
Sets the reading position within the file to new-pointer, where the units are
as with :read-pointer. This operation is for input streams only.
6.1.3 Standard Streams (3.4, Volume 5)
There are about half a dozen special variables whose values are streams widely
used by many system (as well as user) functions. Here are some of them.
standard-input
In the normal Lisp top-level loop, input is read from standard-input (i.e.,
whatever stream is the value of standard-input). Many input functions,
including tyi and read, take a stream argument which defaults to standard-
input.
standard -output
Analogous to standard-input; in the Lisp top-level loop, output is sent to the
stream which is the value of standard-output, and many output functions,
including tyo and print, take a stream argument which defaults to standard-
output.
terminal-io
The value of terminal-io is the stream which connects to the user's console.
In a process which is running in a window (keep in mind that each process
has its own binding stack, and thus can have its own value for a given spe-
cial variable), the value of terminal-io is likely to be that window. For
processes without windows, which don't normally communicate directly with
124 STREAMS AND FILES Chapter 6
the user (like the mouse process), terminal-io defaults to a stream which
does not expect to ever be used. If it is used, perhaps by an error printout,
it turns into a "background" window and requests the user's attention.
error-output
The value of error-output is a stream to which error messages should be
sent. Normally this is the same as standard-output, but standard-output
might be bound to a file and error-output left pointing to the terminal.
standard-input, standard-output and error-output are initially bound to synonym
streams which pass all operations on to the stream which is the value of terminal-
io. That is, they are bound to an uninterned symbol whose function definition is
forwarded to the value cell of terminal-io. So if terminal-io is re-bound {i.e., the
contents of its value cell change) the synonym streams see the new value.
User programs generally don't change the value of terminal-io. A program which
wants, for example, to divert output to a file should do so by temporarily binding
standard-output; that way error messages sent to error-output can still get to the
user by going through terminal-io, which is usually what is desired.
6.1.4 Making Your Own Streams (3.5, Volume 5)
While most streams are actually instances of some flavor, and handle their mes-
sages through the usual message-handling mechanism of calling the appropriate
method, all that's really needed for a simple stream is a function which dispatches
off its first argument (the operation) and calls the default handler if it doesn't
recognize the operation. Here's a simple output stream, which accepts characters
and conses them onto a list:
(defvar the-list nil)
(defun list-output-stream (op ^optional argl &rest rest)
(selectq op
(:tyo (push argl the-list))
( :which-operations ' ( :tyo) )
( otherwise ( stream-default-handler
#' list-output-stream op argl rest))))
As an output stream, the stream is required to support :tyo directly, and to support
the other standard output operations (like :string-out) via the default handler. The
default handler is invoked by calling the function stream-default-handler, with argu-
ments of the stream, the operation, the first argument, and the rest arg.
Section 6.1 Streams 125
Here's a complementary input stream, which reads its characters from a list.
(defvar the-list)
(defvar untyied-char nil)
(defun list-input-stream (op ^optional argl &rest rest)
(selectq op
( :tyi (cond (untyied-char (progl untyied-char
(setq untyied-char nil)))
((null the-list) (and argl (error argl)))
(t (pop the-list))))
( :untyi (setq untyied-char argD)
( :which-operations ' ( :tyi runtyi) )
( otherwise ( stream-default-handler
#' list-input-stream op argl rest))))
Note that :untyi must be supported, and that the stream must check for having
reached the end of the information, and do the right thing with the argument to the
: tyi operation.
6.2 Accessing Files and Directories
Some of the information in this section will make more sense after reading section
3, Pathnames.
6.2.1 Open, and Other Functions for Operating on Files (10, 10.1, Volume 5)
All reading from and writing to files is done through streams. To access a file you
must have an open stream to that file. The fundamental way to obtain an open
stream to a file, whether for reading or writing, is with the open function.
open pathname &rest options
Returns a stream connected to the specified file, pathname may be anything
acceptable to fs:parse-pathname, generally either a string or an actual path-
name object, options is a set of alternating keywords and values, controlling
such attributes of the stream as whether it is for input or output, and how
many bits there are per "character." Here are some of the more frequently
used option keywords:
direction
126 STREAMS AND FILES Chapter 6
usually either :input, to read from an existing file, or :output, to write
a new file.
:byte-size
the number of bits per byte; each :tyo or :tyi operation will deal with
this many bits of the file. Note that this need not agree with what
the host computer thinks the byte-size for the file is.
:if -exists ( Warning: not fully supported by Version 8 UNIX.)
specifies what to do if the : direction is : output and a file with
the desired name already exists. Some of the possibilities are to sig-
nal an error, overwrite the old file, append to the end of the old file,
or write a file with a unique version number.
Most programs do not call open directly. They more commonly use the with-open-
file macro, which makes use of an unwind-protect to guarantee that the stream will
be closed when you're done with it. If you call open directly, you should also use
an unwind-protect to make sure the stream gets closed, because leaving around lots
of open streams can create problems.
with-open-file (stream pathname options...) body...
Evaluates the body forms with the variable stream bound to a stream open
for reading or writing to the file specified by pathname, pathname and
options are interpreted as in open. When control leaves the body, either nor-
mally or abnormally, the file is closed. If a new output file is being written,
and control leaves abnormally (i.e., because of an error or a throw), the file
is aborted.
So if I wanted to write a new text file in my directory on the UNIX host sola, which
contained only the string "Wow. I'm on a disk!" (without the double quotes), I
would evaluate:
(with-open-file (str "s : //usr//hjb//yippee" rdirection routput)
(send str :string-out "Wow. I'm on a disk"))
Or if I wanted to see how many characters into a certain file the first "a" occurred,
(with-open-file (str "s : //usr//hjb//.prof ile" :direction :input)
( loop for i from 1
for char = (send str :tyi)
when (char-equal char #\a) return i))
Section 6.2 Accessing Files and Directories 127
Here are some more functions for operating on files. They generally accept either a
string or a pathname object, and some of them also accept a stream open to the
appropriate file.
renamef file new-name &optional (error-p t)
Changes the name of the file. Meta-X Rename File in the editor uses this
function. If an error occurs and error-p is non-nil, the error is signaled. If
there's an error and error-p is nil, the error object is returned. See the
documentation for details on what happens with wildcard names and links.
deletef file &optional (error-p t)
The specified file is deleted, error-p is as in renamef.
fsrfile-properties pathname &optional (error-p t)
Returns a disembodied property list describing the file. The car of the list is
a pathname for the file's truename, the rest is alternating indicators and
values. See the documentation for fs:directory-Iist for a list of the possible
indicators.
fs: change-file -properties pathname error-p &rest properties
The properties arguments are alternating keywords and values. fs:change-
file-properties alters the attributes of the file accordingly, if possible. (Some
properties are not alterable. Which ones are is a property of the host file
system.)
viewf pathname &optional (stream standard-output) leader
Prints the file on stream. (Use this just for looking at a file, not for copying
it. Its output is not exactly the same as the contents of the file.)
copy f from-path to-path &key (characters :default> (byte-size niU
(copy -creation-date t) (copy -author t)
(report -stream niU (create -directories :query>)
Copies one file to another. M-x Copy File in the editor uses this function.
See the documentation for the details of merging, wildcard names, and links,
and for the meanings of the keyword arguments.
probef pathname
If the specified file exists, returns a pathname for its truename. Returns nil
if the file does not exist.
load pathname &optional pkg nonexistent -ok -flag dont -set -default -p no-msg-p
Loads the specified file into the Lisp environment. (If it's a text file, load
128 STREAMS AND FILES Chapter 6
calls readfile; if it's a binary (compiled) file, load calls fasload.)
6.2.2 Special Messages for File Streams (10.3, Volume 5)
These are some of the operations handled by streams connected to files, in addition
to the general operations described earlier.
:pathname
Returns the pathname that was opened to get this stream. This may differ
from the original argument to open because parts of the pathname may have
been filled in with defaults.
:truename
Returns the pathname of the file actually open on this stream. This may
differ from what :pathname returns because of links, logical devices, mapping
of the "newest" version onto a specific version number, and so on.
:length
Returns the length of the file, in bytes. The number of bits in each byte
depends on how the file was opened. (See the :byte-size option to open.)
:creation-date
Returns the creation-date of the file, expressed in lisp machine "universal
time" units (see "Dates and Times," Part VI of Volume 7).
6.2.3 Directories (11.1, Volume 5)
fs:directory-list pathname &rest options
pathname may be either a string or a pathname object. fs:directory-list
finds all files matching pathname and for each one gets the information that
would be returned by fs:file-properties for that file. It collects all of these
into a list, and adds one element to the beginning of the list with information
about the file system as a whole. So the returned list has one more element
than the number of files. See the documentation for a description of the
options and more details on what information is provided for each file.
fs:complete-pathname defaults string type version &rest options
string is a partially specified file name. fs:complete-pathname looks in the
file system on the appropriate host and returns a new, possibly more specific,
string. Any unambiguous abbreviations are expanded out in a host-
dependent fashion. There are four full pages of documentation attempting
to explain how this happens (repeated in 12.7). Help yourself.
Section 6.2 Accessing Files and Directories 129
6.3 Pathnames
6.3.1 General (12.1, Volume 5)
Just as streams are intended to provide a uniform, device-independent interface
between programs and the different kinds of peripherals, pathnames are intended to
provide a uniform interface between programs and remote file systems. The idea is
to free the programmer from having to keep in mind the format for file names on
the various remote hosts. With pathnames, you should be able to manipulate files
on a file server without knowing anything about that server's syntax for file names.
All pathnames are instances of some flavor, and all the pathname flavors are built
on the flavor fs:pathname. Each pathname has six components which correspond to
different parts of a file name. The mapping of the components into the parts of the
file names is done by the pathname software, and is specific to each kind of host the
software knows about.
The six components of a pathname are the host, the device, the directory, the
name, the type, and the version. So, for example, the pathname corresponding to
the file /usr/hjb/mbox on the UNIX host sola is an instance of flavor fs:unix-
pathname which prints as #<UNIX-PATHNAME "S: //usr//hjb//mbox">. It
has a host of sola, a directory of /usr/hjb, a name of mbox, and a value of
:unspecific for device, type and version. The pathname corresponding to the
file >sys>site>notice.text.8 on the lisp machine glengarioch has a host of glen-
garioch, a directory of >sys>site>, a name of notice, a type of text, a version of
8, and :unspecific again for device.
A pathname need not refer to a specific file. #<UNIX-PATHNAME "S: "> is a
perfectly legitimate pathname, even though it specifies only a host and nothing else.
The conversion of a string into a pathname is usually done by the function
fs:parse-pathname The first thing it has to do is determine the host, since the
method for parsing the rest of the components depends on which host it is. If there
are any colons in the input string, everything appearing before the first colon is con-
sidered to be the name of the host. Parsing of the remainder proceeds according to
the type of the host, and its own syntax for file names. (If there are no colons,
some default value is used for the host — every pathname must have a host.)
Of course, there's no need to go through strings (and worry about the remote host's
file name syntax) at all. One of the selling points of pathnames is precisely that
you shouldn't need to do so. Accordingly, one may construct pathnames in this
manner:
130 STREAMS AND FILES Chapter 6
(f s :make- pathname :host "s" : directory ' ( "USR" "HJB" )
:name "MBOX" :type runspecific)
which returns the same pathname whose printed representation was shown above.
Pathnames are interned, just like symbols, meaning that there is never more than
one pathname with the same set of component values. The main reason for main-
taining uniqueness among pathnames is that they have property lists, and it's desir-
able for two pathnames that look the same to have the same property lists.
6.3.2 Component Values (12.1.4, 12.1.5, Volume 5)
The host component is always a host object (an instance of some flavor built on
net:basic-host) . The permissible values for the other components depends to some
extent on the type of the host, but there are some general conventions.
The type is always either a string, or one of the symbols nil, :unspecific or
:wild. Both nil and :unspecific denote a missing component. The
difference is in what happens during merging (see below); nil generally means to
use the default, and runspecific generally means to keep that component
empty. The symbol :wild is sometimes used in pathnames given to fsrdirectory-
list, and matches all possible values.
The type field gives an indication of what sort of stuff is in the file. Lisp source
files, for instance, usually have a type component of "lisp," and compiled lisp code
a type component of "bin." Since there are some system-dependent restrictions on
how many characters may appear in this field, a canonical type mechanism exists to
allow processing of file types in a system-independent fashion. I quote: "A canoni-
cal type is a system-independent keyword symbol representing the conceptual type
of a file. For instance, a Lisp source file on a VMS* system will have a file type of
'LSP,' and one on a UNIX system will have a file type of '1.' When we ask path-
names of either of these natures for their canonical type, we receive the keyword
symbol :lisp."
The version is either a number or one of the symbols nil, runspecific,
:wild, : newest or : oldest. The first three have the same meaning as for
type. : newest refers to the largest version number that exists when reading a
file, or one greater than that number when writing a new file. : oldest refers to
the smallest version number that exists.
VMS is a trademark of Digital Equipment Corporation.
Section 6.3 Pathnames 131
The device component may be either nil or runspecif ic, or a string designat-
ing some device, for those file systems that support such a notion (VMS, TOPS-20,
ITS).
The name component may be nil , :wild or a string.
The directory component may be nil or :wild for any type of host. On non-
hierarchical file systems, a string is used to specify a particular directory. On
hierarchical systems, the directory component (when not nil or :wild) is a list
of directory level components. These are themselves usually strings. So the path-
name #<UNIX-PATHNAME "S: //usr//h jb//mbox"> has for its directory
component the list ( "USR" "HJB" ). The directory level components can also be
special symbols, as well as strings. :root, for instance, refers to the root direc-
tory on the given host. And : relative followed by one or more occurrences of
:up refers to a relative pathname. So the UNIX pathname #<UNIX-PATHNAME
"S: . .//foo//bar"> has a directory component of (:relative :up
"FOO"). And the lisp machine pathname #<LMFS- PATHNAME
"G:<<x>y>z. lisp"> has a directory component of (: RELATIVE :UP :UP
"X" "Y" ). Other possibilities for directory level components are :wild (for any
single directory), : wild-inferiors (for any number of directory levels), and
partially wild strings, like "FOO*".
6.3.3 Case in Pathnames (12.1.7, Volume 5)
Since the various host systems have different conventions as to upper and lower
case characters in file names, most pathname functions perform some standardiza-
tion of case to facilitate manipulating pathnames in a host-independent manner.
There are two representations for any given component value, one in raw case and
one in interchange case. Raw case representation, which is used internally for the
instance variables of pathnames, corresponds exactly to what would be sent to the
remote machine to find the file corresponding to the pathname. Interchange case is
the standardized form, and is what you get if you ask a pathname for its com-
ponent values. It's also what functions like fs:make-pathname expect (unless you
specify that you mean raw case).
The standardization is simple. Each host is classified as to whether its preferred
case, or system default case, is upper or lower. Any raw component which is in the
preferred case for its host has an upper case interchange form. A raw component
which is in the non-preferred case has a lower case interchange form. A raw com-
ponent in mixed case has an identical (mixed case) interchange form. Since UNIX
hosts are classified as having lower case for the system default, this means that the
raw forms are case-inverted to get the interchange forms, and vice versa.
132 STREAMS AND FILES Chapter 6
The messages for accessing and setting the component values of pathnames assume
that you want to see or set the interchange form, unless you explicitly specify raw
case.
6.3.4 Defaults and Merging (12.2, Volume 5)
In most situations where the user is expected to type in a pathname, some default
pathname is displayed, from which the values of components not specified by the
user may be taken. Most programs maintain their own default pathnames, contain-
ing component values that would be reasonable in the particular context. For pro-
grams which really have no idea of what sort of pathname to expect, there is a set
of default defaults.
The pathname provided by the user (actually, the pathname constructed by
fs:parse-pathname from the string provided by the user) and the default pathname
are then merged by the function fs:merge-pathnames. The details are a little
messy, but the basic idea is that components which aren't specified in the user's
pathname are taken from the default.
6.3.5 Pathname Functions and Messages (12.7, 12.8, Volume 5)
We've already seen three of the most important pathname functions: fsrparse-
pathname, fs:merge-pathnames, and fs:complete-pathname. Here are some more.
fs: make-pathname &rest options
The options are alternating keywords and values, specifying the components
of the pathname. Missing components default to nil, except the host,
which is required. Options allowed are :host, : device, : direc-
tory, :name, :type, : version, : raw-device, : raw-
directory, : raw-name, : raw- type and : canonical-type. So
the device, directory, name and type may be given in either interchange case
or raw case, and the type may also be given in canonical form.
fs:define-canonical-type canonical -type default &body specs
This defines a new canonical type, canonical -type is the symbol for the new
type, the body is a list of specs giving the surface type corresponding to this
canonical type for various hosts, default is the surface type for any hosts
not mentioned in the body. Here is how the :lisp canonical type is
defined:
(f s:def ine-canonical-type :lisp "LISP'
((:tops-20 :tenex) "LISP" "LSP")
Section 6.3 Pathnames 133
( :unix "L" "LISP" )
( :vms "LSP") )
The :host message to pathnames returns the host component, which will always be
an instance of some flavor built on net:basic-host. The messages :device, directory,
.name, and :type return the corresponding component value, with any strings given
in interchange case. The messages :raw-device, :raw-directory, :raw-name, and
:raw-type are similar, but use raw case for all strings. The :version message returns
the version (case is not an issue since versions are never strings). The :canonical-
type message returns two values; together they indicate the type component of the
pathname, and what canonical type — if any — it corresponds to. (See the docu-
mentation for details.)
The messages :new-device, :new-directory, :new-name, and :new-type all take one
argument and return a new pathname which is just like the one that received the
message except that the value of the specified component will be changed. The
argument is interpreted as being in interchange case. You can guess what :new-
raw-device, :new-raw-directory, :new-raw-name and :new-raw-type do. :new-version
and :new-canonical-type also do the obvious thing, and have no "raw" form for the
obvious reasons.
:new -pathname allows wholesale replacement of component values; its arguments
are alternating keywords and values, with the same keywords accepted as by
fs:make-pathname.
There are a set of messages for getting strings that describe the pathname. The
returned strings come in different forms for different purposes. :string-for-printing
returns the string that you see inside the printed representation of a pathname.
:string-for-host shows the file name (not including the host) the way the host file
system likes to see it. There are several others.
:get, rputprop, :remprop and :plist all do the obvious thing with the pathname's
property-list. Keep in mind the distinction between the pathname's property-list
and the list returned by fs:file-properties, or the :pro per ties message to pathnames.
The latter are the properties of a file, and require accessing the host's file system.
The former are the properties of a pathname, a lisp object which may not even
correspond to any files.
6.3.6 Logical Pathnames (12.9.10, Volume 5)
There are some pathnames which don't correspond to any particular file server, but
rather to files on a logical* host. The logical host may then be mapped onto any
134 STREAMS AND FILES Chapter 6
physical host, thus defining a translation from logical pathnames to physical path-
names. This feature improves transportability of code. Take the lisp machine sys-
tem software as an example. Every lisp machine site keeps the source code on a
different computer. But there are many functions that want to be able to find these
files, no matter what site they're running at. The solution is to use logical path-
names: all the system software is in files on the logical host "sys." Each site gives
the "sys" host an appropriate physical host, and then it works just fine to open a
file with a name like "sys: io; pathnm.lisp," which happens to be the file containing
the pathname code. At my site that corresponds to the file ">sys-6>io>
pathnm.lisp" on the lisp machine laphroaig.
The function fs:set-logical-pathname-host defines the mapping of file names from a
logical host to the corresponding physical host. The call to fs:set-logical-pathname-
host is supposed to be placed in the file "sys: site; host. translations." Then if you
call the function fs:make-logical-pathname-host with an argument of the host name,
it will look for and load the appropriate file, thus evaluating the fs:set-logical-
pathname-host. The format of the arguments to fs:set-logical-pathname-host is best
explained by example. This is an abridged version of the contents of "sys: site;
kwc. translations," which defines the "kwc" logical host:
( f s : set-logical-pathname-host
" kwc "
: physical-host "Sola"
translations
' ( ( "distribution; " M//lispm//kwc//rel6//distribution//" )
( "distribution;*; " "//lispm//kwc//rel6//distribution//*//" )
( "distribution;*;*; " "//lispm//kwc//rel6//distribution//*//*//" )
("*;" "//lispm//kwc//rel6//*//") )
: rules
mix
' : new-pathname :name "PUBSYS")
:new-pathname :name "INPUT-ED")
' :new-pathname :name "COMTAB-EX")
.*" :new-pathname :name "CALL-MINI")
: new-pathname :name "MVG-ICONS" ) ) ) )
As you can see, the mapping is done on a directory-by-directory basis. Wild-cards
are allowed. For instance, files in the directory "kwc: distribution; new-class" are
mapped to the directory "/lispm/kwc/rel6/distribution/new-class/" by the second
entry in the : translations argument. The : rules argument allows us to
' ( ( : unix
( "kwc :
:**
; public-systems . *
( "kwc:
:**
; input-editor . * . *
( "kwc :
:••
; comtab-example . *
("kwc:
:**
; call-mini-buffer
( "kwc :
:**
; moving- icons. *. *
See hacker's definition at end of chapter.
Section 6.3 Pathnames 135
specify additional transformations to be carried out in special cases. Since our ver-
sion of UNIX doesn't allow filenames longer than 14 characters, we take advantage
of this facility to define shortened names for the UNIX physical filenames
corresponding to logical pathnames with long names.
Given a pathname for some logical host, the mapping to physical pathname is car-
ried out by sending the logical pathname the :translated-pathname message. An
earlier version of the text for this chapter is in a file whose logical pathname is
#<LOGICAL- PATHNAME "KWC: DISTRIBUTION; NEW-CLASS; STREAMS .TALK" >.
When that pathname is sent the : trans lated-pathname message, it returns
#<UNIX-PATHNAME "S : //lispm//kwc//rel6//distribution//new-class//
streams . talk" >.
6.4 Fun and Games
From The Hacker's Dictionary, Guy L. Steele, Jr., et al:
LOGICAL adjective.
Conventional; assumed for the sake of exposition or convenience; not the
actual thing but in some sense equivalent to it; not necessarily corresponding
to reality.
Example: If a person who had long held a certain post (for example, Les
Earnest at Stanford) left and was replaced, the replacement would for a
while be known as the "logical Les Earnest." Pepsi might be referred to as
"logical Coke" (or vice versa) .
At Stanford, "logical" compass directions denote a coordinate system in
which "logical north" is toward San Francisco, "logical south" is toward San
Jose, "logical west" is toward the ocean, and "logical east" is away from the
ocean — even though logical north varies between physical (true) north near
San Francisco and physical west near San Jose. The best rule of thumb
here is that El Camino Real by definition always runs logical north-and-
south. In giving directions, one might way, "To get to Rincon Tarasco Res-
taurant, get onto EL CAMINO BIGNUM going logical north." Using the
word "logical" helps to prevent the recipient from worrying about the fact
that the sun is setting almost directly in front of him as he travels "north."
A similar situation exists at MIT. Route 128 (famous for the electronics
industries that have grown up along it) is a three-quarters circle surrounding
Boston at a radius of ten miles, terminating at the coast line at each end. It
136 STREAMS AND FILES Chapter 6
would be most precise to describe the two directions along this highway as
being "clockwise" and "counterclockwise," but the road signs all say "north"
and "south," respectively. A hacker would describe these directions as "log-
ical north" and "logical south," to indicate that they are conventional direc-
tions not corresponding to the usual convention for those words. (If you
went logical south along the entire length of Route 128, you would start out
going northwest, curve around to the south, and finish headed due east!)
Section 6.4 Fun and Games 137
6.5 Problem Set #6
Questions
1. A. There is a function named print-disk-label that, when called with no
arguments, prints on the screen a listing of the contents of the fep file
system. Find a way to print this listing to a file instead. (Hint: check
out the optional arguments to print-disk-label, and use with-open-file.)
B. There's another function named si:print-login-history that prints a list
of everyone who has logged in to the local machine since it was cold-
booted (and the contents of the login history when the world load was
made). This one has no optional argument for what stream to do the
printing on — it always prints to standard-output. How can you get it
to print the listing to a file? (Hint: make standard-output point to a
file.)
2. Suppose there is a file whose contents are numbers in the range
-2,147,483,648 < n < 2,147,483,647 (32-bit integers). How can we read
the file into an array of fixnums? It'd be convenient to open a 32-bit stream
to the file and just do : tyi's or a : string-in, but most file servers won't
allow a 32-bit stream. We'll have to use a 16-bit stream. One strategy is to
read two 16-bit bytes at a time and build a 32-bit number by shifting one
number 16 bits and adding them together. This will work, but it's awfully
slow. Can you think of anything better? (Hint: think about displaced
arrays of different types.)
138 STREAMS AND FILES Chapter 6
Solutions
1. A. (with-open-f ile (str "s://usr//hjb//disk-label"
: direction : output)
(print-disk-label si : *boot-unit* str))
B. (with-open-f ile (standard-output "s ://usr//hjb//logins"
: direction : output)
(si:print-login-history) )
2. The slow way:
(defun foo (file ^optional array)
(with-open-f ile (str file :characters nil :byte-size 16.)
(or array ( setq array
(make-array ( // (send str : length) 2))))
( loop for i from 0
for c1 = (send str :tyi)
for c2 = (send str :tyi)
while c1
do (setf (aref array i) (+ d (lsh c2 16))))
array) )
The fast way:
(defun bar (file ^.optional array32 array16)
(with-open-f ile (str file :characters nil :byte-size 16.)
(or array32
(setq array32 (make-array (// (send str : length) 2)
: initial-value 0)))
(send str : string-in nil
(or array16 (make-array (* 2 (array-length array32))
:type 'art- 16b
:displaced-to array32)))
array32) )
Chapter 7
THE TREE EXAMPLE
This chapter is very much like the graph example two chapters back — a later sec-
tion contains a code listing, and this one describes some of the new features* of the
code. Much of the code was copied directly from the graph example (with "graph"
changed to "tree"). The most interesting of the new parts have to do with menus.
Once again, if your site has the tape for this book, you can load the code by using
the CP command Load System tree [or evaluating (make-system
'tree)]. Once the code has been read, start the program by evaluating (send
(tv: make -window 'tree-frame) : select).
7.1 The Nodes and Arcs
•root* and *the-real-root*
These two variables are declared at the very beginning of the file. Rather than
keep a list of all the nodes, as "graph" does, we simply keep track of the root of the
See hacker's definition at end of chapter.
140 THE TREE EXAMPLE Chapter 7
tree and follow the connections from there. The value of *the-real-root* is constant
throughout the lifetime of a given tree. It changes only when we throw away the
tree to start another. *root* refers to the node which is displayed at the top center
of the window. Initially, this is the same as *the-reaI-root*, but you can change it
to be any arbitrary node in the tree. That way you can move your window around
over a tree too large to be viewed at once.
The node defflavor
The connections between nodes {arcs, in "graph") are no longer real data objects.
Each node now knows directly which other nodes it's connected to, instead of know-
ing which arcs it's connected to. children is a list (possibly empty) of the direct
inferiors, and parent is the superior (nil for the node which is the value of *the-
real-root*). The x-pos and y-pos instance variables have been thrown out, since
nodes no longer have fixed positions. Each parent determines where its children
will be drawn. The space -requirements instance variable somehow packages up
everything a node's parent needs to know about the node and its children in order
to determine where to draw it. The first time this information is requested it is
recursively calculated, and saved for future requests. The saved info is flushed
whenever something happens that would invalidate it (such as a change in the
number of children), so that it is recalculated the next time someone asks for it.
Consequently, anyone needing the information should use the : get-space-
requirements method, which calculates if necessary, rather than looking at the
instance variable directly.
The :f lush-space-requirement method
This method is called whenever the space-requirement info has been invalidated. It
sets the instance variable to nil, and recurses upward, because anytime a node's
space requirements change its parent's also do. It gets called whenever the node's
label is changed, and whenever a child is added or removed. (And whenever any of
these things happens to one of its descendants.)
Fancier format directives
The format statement inside the : print- self method uses two features you may
not have seen before. "- {...-}" is an iteration construct, which takes a list as an
argument, and repeats the interior of the { }'s until the list elements are exhausted.
"-©[...-]" checks the next argument, and if it is nil, does nothing. If it is non-
nil, the argument is not used up but remains the next one to be processed, and the
interior of the [ ]'s is executed.
The effect here is to print a list of all the children (using -A so that only their
Section 7.1 The Nodes and Arcs 141
names are printed), with all but the last in the list being followed by "; ". Both
- { and - [ exist in several forms, with and without : and/or @, allowing various
kinds of iteration and selection.
Drawing
The methods : draw-self -and-children and : get-space-
requirements have all the smarts. It's complicated, but I don't think it's partic-
ularly interesting. I won't go into it here since there's little of general value. Do
feel free, however, to look through the code on your own.
7.2 The Windows and the Mouse
As with "graph," the first half of the file is adequate if you're willing to type awk-
ward forms to a lisp listener. The second half provides a better user interface,
mainly using the mouse.
The tree-frame defBavor
This time we have three panes instead of two. The new one is a command menu.
Many system utilities based on frames have a command menu pane, including Peek
(Select P), Zmail (Select M), and File System Maintenance (Select F). Command
menus differ from other menus in that they stay exposed indefinitely, becoming
active only when you move the mouse over them, and in that they don't themselves
produce any action when you choose an item, but simply stuff a blip into
somebody's io-buffer. It's up to whoever reads from the io-buffer to do something
with the blip (often sending it back to the command menu with an : execute
message) . There are more details on how menus work later in the chapter.
Shared io-buffers
For the process running in the tree pane to see the blips from the command menu,
the two windows must share one io-buffer. The : after : init method on tree-
frame arranges this. I could have built tree-frame on tv:bordered-constraint-frame-
with-shared-io-buffer instead of plain tv:bordered-constraint-frame, but then the lisp
pane would also share the one io-buffer, and that wouldn't work. (Some input
intended for the tree-pane's process would be read by the lisp-pane's process, and
vice versa)
Tree-window's : main-loop
142 THE TREE EXAMPLE Chapter 7
There are now two kinds of blip to watch for: the familiar : typeout-execute
blips from the mouse-sensitive items, and new :menu blips, from the command
menu. For now, all you need to understand about the action taken for :menu
blips is that the command-menu itself is sent an : execute message with an argu-
ment of the menu item that was chosen.
Tree-window's : refresh
Drawing the tree is accomplished by sending : draw-self -and-children to
the current *root*, which will recursively send the same message to its descendants.
The initial arguments put the *root* at the top-center of the window.
The menu-item-list
Now we get into menus. First off, I should mention that the documentation on
menus — Part III of volume 7 — is not too bad. (In fact, much of this stuff I had
actually never messed with until the day before writing the first version of this
chapter. I just read the documentation and did it.) The basic idea is that menus
are special kinds of windows that maintain a list of items to choose from, and do
something appropriate if you click on one of the items. The list of items is kept in
the instance variable item-list; once a menu has the right item-list, the : choose
method does the rest: it exposes the window, waits for you to click on some item
(the : mouse-buttons method tells it when that has happened by setting the
chosen-item instance variable), and sends itself the : execute message with an
argument of the chosen item. The : execute message does something appropri-
ate, depending on the type of the item.
And now for the format of the items on the item-list, and what it means to "do
something appropriate" (the task of the : execute message). The simplest kind
of item is just a string or a symbol. The string or symbol is displayed in the menu
as itself, and executing such an item just means to return it. The item may also be
a list (or dotted-pair) of two elements; the first is the symbol or string to be
displayed, the second is what is returned by execution of the item. The most gen-
eral kind of item is a list of three or more elements. The first is what to display in
the menu, the second is a keyword for the type of this item, the third is an arbi-
trary argument whose interpretation depends on the type of the item, and the rest
of the list is alternating pairs of modifier keywords and values. The keyword in
position two may be any of: : VALUE, :EVAL, rFUNCALL, .-FUNCALL-
WITH-SELF, : NO-SELECT, : WINDOW-OP, :KBD, :MENU, or : BUTTONS.
The first three are the most commonly used. : VALUE means to return the argu-
ment (the third element of the list), :FUNCALL means to funcall the argument
(presumably the name of a function) and return its return value, and :EVAL
means to evaluate the argument (presumably a lisp form) and return its return
Section 7.2 The Windows and the Mouse 143
value. The only defined modifier keywords are :FONT and : DOCUMENTATION.
:FONT specifies which font should be used to display this item in the menu,
: DOCUMENTATION is what appears in the who-line when the mouse is over this
item.
If you look at the *tree-command-menu-item-list*, you'll see that all three items use
the general form. Two of them are of the rFUNCALL type and one is of the
: EVAL type.
tv:menu-choose
The easiest way to use menus is to call the function tv:menu-choose with an argu-
ment of a suitable item-list. This function will allocate and expose a menu, set its
item-list instance variable to be the argument you supplied, and send it the
.•choose message. Its : choose method then waits for you to click on an item,
and sends the menu : execute of the chosen item. If you look at the : mouse-
click method for tree-window, you'll see that clicking right anywhere except over
a mouse-sensitive item does just that. It calls tv:menu-choose with an argument of
*tree-command-menu-item-list*. (The use of process-run-function is necessary
because the : mouse-click method runs inside the mouse process — without it
the mouse process would be hung until tv:menu-choose returned, but tv:menu-
choose would never return because the mouse process is hung.)
The command menu revisited
The command menu pane uses exactly the same item-list, as you can see from look-
ing at the tree-frame defflavor, in the : panes section. But as I said earlier, com-
mand menus work differently; you don't send them the : choose message. They
stay exposed indefinitely, and when you click on one of their items they just stuff a
blip into their own io-buffer. In our case, that means the tree pane's io-buffer.
When the :main-loop finds it there, it sends the : execute message back to
the command menu (with most menus the : choose method does this for you),
and the chosen item is executed normally.
7.3 Fun and Games
From The Hacker's Dictionary, Guy L. Steele, Jr., et ah
FEATURE noun.
1. An intended property or behavior (as of a program). Whether it is good is
immaterial.
144 THE TREE EXAMPLE Chapter 7
2. A good property or behavior (as of a program). Whether it was intended is
immaterial.
3. A surprising property or behavior; in particular, one that is purposely incon-
sistent because it works better that way. For example, in the EMACS text
editor, the "transpose characters" command will exchange the two characters
on either side of the cursor on the screen, except when the cursor is at the
end of a line; in that case, the two characters before the cursor are
exchanged. While this behavior is perhaps surprising, and certainly incon-
sistent, it has been found through extensive experimentation to be what most
users want. The inconsistency is therefore a feature and not a BUG.
4. A property or behavior that is gratuitous or unnecessary, though perhaps
impressive or cute. For example, one feature of the MACLISP language is
the ability to print numbers as Roman numerals. See BELLS AND WHIS-
TLES.
5. A property or behavior that was put in to help someone else but that happens
to be in your way. A standard joke is that a bug can be turned into a feature
simply by documenting it (then theoretically no one can complain about it
because it's in the manual), or even by simply declaring it to be good.
"That's not a bug; it's a feature!"
The following list covers the spectrum of terms used to rate programs or por-
tions thereof (except for the first two, which tend to be applied more to
hardware or to the SYSTEM, but are included for completeness):
CRASH
BUG
CROCK
WIN
STOPPAGE
LOSS
KLUGE
FEATURE
BRAIN DAMAGE
MISFEATURE
HACK
PERFECTION
The last is never actually attained.
7.4 The Program
Section 7.4
The Program
145
146
THE TREE EXAMPLE
Chapter 7
0)
,_l
^x
0) 43
CD
•H
o>
0) <D
s
0) -»
C
■O o
T3
•H
U
C
73
43 73
Oi O
C +J
0)
0 id
01
73
•p
0
0
•d 0
0) rl
0 0^
rl
(3 0) 0>
01
<d ai
■H
>,
rH C
rl
O rH
13
43
C
rl 43
0)
P
id
■P id
CO rl 0)
r-\
CO >,+J
0
-p <-.
43
•H
rH
O >w
■H £1
0)
•rl
•H 43
o) c
0) ^
P
CO
ft
c o
0 c
43 oi id
T3
43
43 4h
13
43 W Oi
0
CO
Xi -rl
H-> 43 rH
0
0
-P -0 0
0 0)
■P 0 C
4-1
ft
•rl
£ tH
•P Xi
■P
C
0)
C rl
•rl
0
73
4-> 0)
•P
4H 4-1
CO
43 0) 3
id
«« X O
P
•H -p
C -H
O 0> 0
0)
p
CO
0 id id
p
0
CO
S 0)
■H >
rl
43
•H
0 CO
•H 0)
e ft
0
0
•H
s
CO v fi
P
T> t3
43 rl
u co
0
M
CO -H
CO CO
rl 0) -P
T3
0> (V
■P 0>
to 0)
M
(V
0 rl
> o>
O > C7>
C
(3
T3 0) 43
43
•H 43 rH
c
0
73 0
0 73
•h a
•H
id
0) O -P
4-1 -P
rH +J Id
0)
•H
u
0 ft
rl 0
rl 4-1 0)
0) (d ^
•H
O
fl
•p
C
c
OI -H rH
Tl
<D
(3 ft
4H
Id 73 -H
p
B
73
c
4H
0)
T3
co u
0) -H
C 4-»
a)
4H (3
a) -p
CH C
fh
0
0> 0)
O
'- «3 rl
p
0
43
0 <d
o> c
•H -H O
<d
C
O H 45
id co
-« ai
(d
43
■P
5 0)
— C
id (d -P
ft C T3
0>
CO
— rH
■P o
-' -P
ft
CO
ft -P 0>
CO 0)
rH ~
rH
0)
43
CO 01
0) id
co C
to
•H
co a oi
ft
•H CO 0>
a
u
>,
o
rH A
43 n
Ol +J 0)
•H
43
0 0
rH ft 43 0) C
a
C
rH
■H
0) Id
•O
•O C -O
■d
P
rH N +J
(d <d
O C -H
id
id
P
43
X rH
co id
O 0i C
id -h
•P 43
•H C
1
X
P
c
5
•H
rH
C rl 0>
<v
>> -P
0
rH rH -H
IV
to
0>
ft C
01 C
id ft 43
43
coo)
■P 43
rH g
1
S3
H
c
0)
X 0)
rl ft 01
0 43 rl
O
(d 0> rl
•H
M
•H
C 0)
■H d)
0) T3
0 T)
n -a
0) -H
e c oi
o
43
3
•H >
ft S
43 co -^
■p
a)
•H rH rH
43 43
CO -H 4J
ft
0
5
>- 4J
4->
■P »
■0
rl Id "H
■P »
4-> 01
id
0
0
0
4-1 0)
0 0> CO
•>
0)
0 -P 43
01 O 73
u
-o
0
73
0> £1
O X)
13 rH
Cn
0)
43 0 O
6 -
C 0)
0
&>
0 -d
c
N
4-1 0 0)
C
(3
■p
0 M
0 C rl
en
_c
0
■H
•H CO
rl CO
OCX
•H
rH 01
u 0>
C 0
id
C
2
CO rH
<U rH
■H
rl
0)
id oi 43
4H 43
(0 0 4-1
OQ
rl
01
43 0)
■P CO ft -P
0
■P 43 -P
4-»
id o >-
0
o;
<D
a)
0) X
§ X
CO -H
to
id
0 -P
CO 0)
43
.a.
•H
A
43
43
43 -H
3 -H
•rl 43 C
ft -p
rl t^
0) 4H
Cm
i-l
p
p
■P
4-> ft
C ft
rH 4-> -H
id
CO
<4H rH
0) 0
4-1 43 rH
w
CO
u
id
o
oi o <d
43
■p -p >,
4H +J
4H
•H 4-»
•H 4-> 0)
CO
73 Ol
^.
CO 43
73 0
c c o>
CO
a)
•H
ft
id -h c
3
a)
43
•• rH XI
CO
> -rl
*-» -H
0>
■p
CO 0>
•rl C
0> id 73
4J 73
<0
-P id -o
43 0)
■H U rj
C id
M
CO
0" of 0)
■P rl
43 73 rH
01 u
o
id
T3
o
rl
ID
e -p c
1 t-*
C0 rl C
<d C
&
a)
rH
*
oi c
rH 0) 0)
•H
0> 43
•H 0 -H
4H
ft 0)
rl
•*>
>,
CO
0) T3 O
C O
Q) «_- -
rH 73
(U
4->
3
C id
•H
73 CO
0) rH
CO
CO
•H
0) 0) ft 73 CO
0 Cr
43 -H
H
-0
> O CO
73 -P
C a> c
•d 43
J
a)
id
•H CO
Id -H
rl id
rH O
e
O
o
U
r-i
4H 0> 0>
ft
CO 73 73
id
o
i
•H
T3 43
rH
•H rH C
CO CO
OI
CO
*-» T-
0
C
M-l H->
(0 rH
43 -H 0)
01 0)
"8
* —
o
TJ
O CO
rj Id
4-» 43 O
r-t rH
a)
CO CO
co *
O
CO
+1 ...
rH
U CO
43 43
s
43
rH
3
Ot
n
p
■P -H C
ft C
4-1 to
id id
-P
•H
•H *
* (3
i
(3
CO 0>
id
•H d) 73
•H -H
•«
C
rH
73 0)
0> -H
1
0)
•H 43 rl
■P 43
43
IH rl
a
c
■rl
Id N
c o
B
rH -P T)
CO 4-1
rl 4J rH
•d <d
CO
■H
c
rl -rl
•h id
— B
0)
•H rH
•H
0 rH
> >
•H
P
1 CO
O ft
r-t -H
iH
Id » -H
rH 0)
4H Id
i i
rH
0
*
0) 1
id co
•H — (3
■H
43
O
- o
01 0)
id
CO
0
2
•o c
ft i
(3 rH -rl
-~*
0
CO rl O
co id
C 4H
o o
•p
(V
i-l
-^
0
O -H
CO rH
•H e
rH
cr
•H 0
0 ft
0> 43 0
c c
(V
0
rH
■o
C 0>
i <d
c c *
■H
0)
X! 0)
0 co
rl O
id <d
N
u
H
■H
c
1 rl
rH +J
(V
13
rl
CO -P 43
•H
73 (d -H
4-1 4-1
4-1
id
C
■H
l§
Id a
rl -P CO
■H 01 -P
> 0)
rH 01 -H
CO CO
0)
»
O O
0>
TJ (3 D
i-H
01
43 d
01 rl
•H 73
c c
X
4-1
H
*
e i
•H N
X)
H lid
0)
0
Eh 0 *H
U 0 43 4H (d
•H -H
id
0
p
0)
•H 0)
-P -H
0
■h nd
42
<d
■-■ +J 0
ft e
O O rl
1 1
-p
>^
43
0
0
a)
rl
C 73
■H O
U U
0) O
c
43 id id
t) ft rl
id
ft
to
0) 01
id
P
rl
■P
e c
> rH
rl
-^
•-'
••> •• ••• — »
— 43 43
CO
rH
*
*
*
* *
* *
o
>
^
— itj as
•P 4-1
1
CO
ri
M
rl
u u
u u
id
4J 4-1
•H
id
m
id
id <d
id id
rH
01 ai
1
■o
>
4-1
>
4H
>
4H
> >
4H 4H
> >
4-1 4-1
4H
4-1
CO 0^
<D
0>
0
0> 0)
U 01
01
!I
!I
73
X3
•o
73 -d
•O T3
■o
Section 7.4 The Program 147
♦J
...
g
s
UH
i
p
•H
a
*
CD
»-»
U
a)
a
a)
<N
u
o
■p
rH
CD
0
CD
£1
c
--»
£
P
13
(0
c
rH
■H
•H
CD
rl
£1
Q)
■d
P
P
rH
en
rH
V
■H
c
CD
5
.-»
ft
— 43
a;
•Q
<-»
I
CU U
rH
1
<t5
rH
V
to
>1
UH
u
a, cd
Cn
rH
p
•H
tH e
C
CD
•H
c
fl
Eh Iti
■rl
P
5
1
CD
e
to
03
•• C
P
<-\
CD
rH
iB^
to
*H
•d
■H
rl
CO
••
0
0)
■p
„,
C
•H
CD
« «
*
A
(0
CD
»-»
cr
rl
Eh r->
s
P
0)
rl
to
CD
0
to l
0
en
P
0
p
Sh
a
t3
c
O
a
— »
C
i
Cn
Pn •"
C
CD
en
CD
CD
•H
J ■->
* *
•H
.—*
H
co
■H
to
e
0
W @J
to a)
S
CD
1
«
p
0
it)
s
CO I
3 N
-^
P
CD
-—
C
rl
ft
03
<-•
— <
•H -H
CD
0
42
CD
CD
■H
to
CD
—*
•a to
<D
^-~
c
P
T>
r-i
to
e
3
1
rl
—^
Eh —
It 1
iH
*
en
0
CD
3
CD
&
45
P
z
O i
u c
-P
to
■H
0
(3
X)
•H
rl
CD
to
to
T3
w ^
0)
1 -H
*
;3
p
id
TJ
■H
rl
0
CD
T)
■d
at en
•H
*J
rH
03
3
rH
rH
e
« t -
0
•d p
n
T3
to
en
1
rl
cr
CD
■H
UH
^^
its
° ^ ~
a
0 i«
c
ffl
CD
c
--~
en
«— «
P
I
CD
U
C
UH
c
1 < —
a e
CD
P
u
•H
*
Pi
CD
P
rl
it)
rH
§
2 i --
10
to
1
<A
T3
*
■H ~
^-
to
CD
|
ft
to
P
CD
9
B= g
0
i d
CD
u
0
c <^
to
CD
to
P
C
CO
•H
T3
0
T3
P <P
to
CD
U
1
G
CD
1
z e a>
T3
e o
\
O
-~-
o
S3
rH
<0 rH
3
rl
rl
it)
.£
CD
rl
-p
rH
5 03 P
4
•H (3
V,
c
*i
u
•H
•rH
3 CD
•H
CD
a
to
e
its
c
CD
« a) -p
M
c *
■H
Its
^-
s
C
= m
«d
P
to
3
CD
ft
•H
X)
1 rl to
I
■H
i
c
1
*
IH
*
rH
rl
M
it)
O -P
4->
a +
•H
to
rH
CD
>
p
03
?
X!
UH
•H
■d
ftrH
Z CO CD
0)
e
3
CD
CD
UH
i
0
to
3
c
0.H g
to
r-i
•H
•H
43
P
•H
p
T3
0
er <u
P
1 Eh 4-» US
CD
<v
X
Pi
CD
T3
Its
P
P
CD
CD
(3
rH
CD
0)
to
CD
0
*» « c
M
A
(0
•H
T3
*
rH
0
to
-a
•H
UH
X}
rl
^^
T3
-^
uh h e
CO
03
e
s
0
U
c
0)
0
IS
0
i
0
•H « P O
"§
d
rH
*
c
P
P
rl
a
1
a
CD
P
C
CD
43 Cu 0 C
•H
CD
0
0
>
CD
UH
U
c
s
(0 •• UH -H
43 d
UH
fl
G
C
p
CD
rH
it!
CD
03
fl) H w ^
4J
0
•H
T3
P
P
■d
rl
CD
T3
ft
M
X)
C
rH CO ft
0
P
0
0
P
to
0
to
it)
0
to — ~-
S
cr
P
CD
■d
A
P
T3
A
P
cr
ft
p
■*■*
UH
c
p
CD
to
c
CD
T3
C
CD
p
■d
CD
P
•rl
3
a;
E
0
e
(3
CD
e
CD
a
B
0
%-»
IH
to
UH
.»
CJ
UH
*
to
UH
a
03
UH
rH
0)
CD
CD
CD
CD
T3
T3
T3
HD
T3
148 THE TREE EXAMPLE Chapter 7
to -h g»
4-)
,_»
•H
0
*-»
X!
to
tj
s
o
&>
3
id
•H
13
•H
•rl
^-^
rl
rl
•H
CD
TJ
o
o
1
£
(0
iH
•rl
rH
«J
*
^
(V
P
>>^-
c
X
>1
O
P
1 '-»
•rl
P
^
P — »
rH
i
rH
Id
a
to
rl
«
CJ>
•rl
u
X —
CD >,
C 1
£
^»
o
0
<^
p
O
to
•H
•rl
CO
+
X X
■a
s
P
rl
n
El
«-*
I CD
C
•H
a
CD
P
■H
> C
•H
TJ
o
«H
C TJ
c
CU
>
A3
uS
•rl
CD
e
T3
CD
C X
1
TJ
>■,
0)
p
> J
c
>,
to
iH
X
0 CD
a>
x
P
H
«
TJ C
rH CO
I
13
3
B
«"^
c
d) ^
^
»
CD
cr
CN
•H fc
A
0
E
CD
CM
> .
id \
tj
CD
iH
f-i
rS
rH \
to
13
rl
1
*
to
id
£ ^
0
•rl
•H
CD
P
P
— CD X
&>
•rl
>
3
U
c
0
CN rl
C >i
tj
cr
id
*
CD
P
TJ CO
•rl
It)
<D
ft en
E
1
^H S
iH 1
H
rl
to
c
CD
CO
P -H -rl
>^
P — '
a
1
i
■H
iH
iH
.C 43 TJ
(0
X
CD
rH
•p
O
■H
O
&> t> a)
M3
rl
It)
CD
03
d
•H
•H 1 U
TJ CO
1
tj
n
&
a
•—
cr
rl
U TJ
O TJ
(D 0
rH
TJ
to
u
CD
CD
(3 S
rl -H
•rl
««»
•H
1
C
iH
>H
cr p (d o
TJ <d
CD TJ
M4
,13
>(
>
4H
H
•H
c
0)
4H 1 T3
c P.
P id
rH
O
•rl
rH
it)
1
rH
■rl
in
CD HH (3
■rl
(3 in
ai
X
■0
CD
o
>,
id
rH rH -rl
i *
0
to to
■0
c
CO
•H
g
N>
-p
CD »
O X
d)
c
S
•H
-P
>>
TJ
\
4H
+ CO
*" X
1
rH 0)
It)
0
TJ
iH
c
■H
CD
^^ 1 CO
>> +
•O TJ
1
T)
rH
a
CD
+
cu
>
r-\
S CD
<w 0)
id ~
(3 0
<M
c
■rl
CD
>
~-^
iH
■rt
X
\ id TJ
iH rH
rH
<D C
H
•H
c
CO
*
TJ
TJ
•H
+
\ U 0
CD U
ft~
r3 ••
CD
*
—
II
rH
c
•H 1
-^
— TJ C
V) U
to to
CO
+
■rl
■H
>o ~^
• • 1
1 -H
•H 3
4H a
IH
TJ
-^
^43
id
II
II P
S u
TJ -H
•H 0)
*
H
C
I
CJ
C
U II
o o
id i
.. <o
1 -P
(0
CD
•H
V
■P
■H
■P
X CD
TJ id
(d
Tj -H
rl
CO
A
a
X
fi
C P
A
i tj a
£ iH
S3 I
•0
1
•H
CD
■H
cr
•H IW
&> ?
•• M
O
d) <D
St
&>
1
C
CD
CD
•H
CD CD O
TJ
TJ X
10 >
A3
c
>>
0
iH
H rH
rl
C to O
0) ••
a
•• -rl
CD
rl
(3 -rl
A
TJ
■H 1
P
tj TJ
CD U
4H
V
M
iH
rl U
rl
rl
O >
> — '
S -H
0
U S
CD
•H
0
0
0 0
0
0 o
(3 0
0 E
fi
•o p
N
»
4H
4H
•4H MH
MH
>*H TJ
■Z t3
rH TJ
TJ -H
UH
rH O
•H
c
CD Pi
fl n
rH
•H 0
0
ft
TJ -rl
A CD
•rl ft
tj
CD A U
e
8
0 >
a] to
* ••
0
CO
O P
CD
^
rH ^
.£3
CO
e
H
P TJ
tj
p
T3
c .2
^
^^
CD (3 TJ
a
(V
a
CD TJ
e a)
C
01
E
CD
■S w
uh to
ItJ
to
MH
CO
S
Section 7.4 The Program 149
••
P
o
X
■d *
S
G cr.
0> G
i)
X
<»^>
0) -H
u
03
(N
~ O
a
10
e
(0 '--
0 <N
ft CO
CO
to
>, 0
'0 rH
rH
g
X)
03
C ^
10 <d
03
o
P
CO
P 4->
P
O (N
ft to
1 (3
0
XS
rH
X o
P
03
03
03
-^
X o
(0 N
rl
P
p
ft
-~~
e -h
0
1
0
O
--^
<N X
H
CO
1 u
p
X
P
P
4H
u
■H
G
4-1 O
G
03
r-t
(N
a
X3
G .G
■H *
„
•H
e
03
to
0)
CO
t- u
to —
03
CO
0
a
cu
ft to ~
n
P
• g
G
tn
p
u
*-»
--»
X3
-p
0
•H
to
G
c
rH
>^ O (N
g
rH
rH A
X>
cr
•H
•H
*
to
T3
»-»
CO
•H
ft >,
0
■H
•rl P
03
CD
X>
to
p
p
rH
c
p
X
«- >^
£
G
A
P
P
X)
xs
X)
to
C
•P
<D
c
U
CO -» (N
0)
O
03
03
03
■H
T3
<D
X!
P
01
0 <N r- X
P
^-*
rH
rH
0
a
rl
P
rH
rH
e
O "O
s
01
ft ^ to
■H
•H
G
•rl II
u
-p
■H
D
rH
0)
>
X<2&^
3
en
G
0)
G
c
p
0
X
^
P
•H
p
0
cr
G
P
&>T3
•H
o
p
03
G
O
•H
A
•H
e
»- X X
<u
co
•H
rH
X)
rH G
g
.G
c
e
P
■-*
4H
3
-o
O
-~~
3
CD
p «-
p
+J
XS
■H
rH
■H -H
CD
0)
■H
i
G
rH
cr
<H
r-{
cr
rH
P
-<- r- X
i
G
03
G
■H
G -0
co
o
<v
4H
P
^-.
0)
0)
•H
-o
•H
CD
•H
S >. P
<u
<D
P
^
XI
— ^
flj
o
>d
c
CD
T3
to
p
x:
rH
C
P
C
O 0)
u
e
CD
U
0) 0)
ft 01
«
•P
P
rH
i
U
•H
P
x> ^- to G
03
0)
03
^
U
O ft
II
CO
a
P
■H
P
cu
i
X\
P
(1)
—
c
G X CD -H
a
p
c
03
g
(0 1
CO
(1)
.£
C
o
0)
u
G
0
CU
CD
•rl «^ X> rH
co
•H
CD
a
•H
ft u
X?
p
P
N
r^
<J
0)
03
>
aj
03
p
P
S 0 1
1
G
~^-
P
CO
to o
03
o
+
u
■rH
rH
1
c
p
a
0
cr
p
a
<D
03
— XJ G *
p
cr
X)
|
O
~ ,C
P
0)
._ •
cu
e
T-i
T3
0)
03
to
e
rH
03
CO
rH
a
G 1 03
0)
0)
0)
rH
g
rH
rH
•H
id
13
P
a
1
0)
ID
a
CU
CO -H 4H P
Cn
P
o
■H
P
u u
P
rH
I
rH
X
c
03
T3
i
XI
p
T3
i
-d
T)
T3
CD A 0 TJ
(0
-G
0
0
0 0
0
0
0
It)
•H
rH
p
CO
^-^
p
CO
c
■d 1 1 ••
0)
a
O
'tH
4H 4H
4H
O
to
O
e
4H
■H
0)
3
0)
rJ
CD
0 CD 10
CD
u
CO
*
0)
^1
to
rH
<D
C
CO
rH
0)
CO
G G <D >
T3
03
r-(
4->
a
13
O
4H
n
0)
4H
T3
«-»
1 rH tn O
0
a
g
rH
CO
0
0
0
P
O
V (0 X) XJ
G
V)
3
•H
0
C
T3
T3
G
•o -o
C
P
O > CD G
0
c
rH
rH
rH
rH
4h
rH
<-{
4H
G
CD 1 1 -rl
4H
■H
•H
r-{
•H
•H
r-i
'V
G CD x) S
X)
(1)
_
•o .c
^1
1'
n
X! X\
0)
T3
P
G rH G
0
N
4-4
0
U
O
W
0
O
o
CO
O
03
0 ft -H X)
.G
■H
p
•H
J3
-C
£1
a
O -H 4H G
P
0
0)
p
^1
-o -o
p
cr -o
T3
P
4J *-- CD
a;
B
rH
OJ
CO
c
c
0)
4J
C
c
0)
T3
G rH 10
g
CD
e
3
0)
CD
e
CD
d)
0)
e
G
G G —
4H
e
4H
a
CO
CO
4H
to
CO
CO
4H
■d
4H 6
0)
D
0)
0)
<D ~
X)
T3
T3
X)
X)
150
THE TREE EXAMPLE
Chapter 7
0
^-»
ft
»-»
X
CM
^ —
u
■CJ —
CO
TJ XI £i .G .G
a
+J 4-> 4-» -P
* tx> cj> &1 &>
>,
~ G G G G
a> cu cu cu
^ rH iH iH rH
CO
0
^
ft
t- t- CN <N
X
X >H u u u
,_
H ~
*■"• * fl "O t) "o
T—
T~ * * # *
0) (0
(Q + «_- ^ ^ —
OJ 0
•O ft
ft" S \ N N
O X
>>P \ \ \ \
g
i cn
cm tr
>4-l 0)
0) 01 r- r- (N CN
0 0
O -H (0 (0 10 0)
1 ft
ft— 0 0 0 0
>, ft ft ft ft
CO X
V
X! X >. X *
0> I
1 +J
X) ~
w- (J* + + 1 1
CU
T> TJ
•O rH tO
G —
w ^ 0)
•rl —
n
M-t
iH
to
G P
>
0 CU
« —
4-1 rH
0) ~
TJ
id
u
p
(0
a
o
o
v u
%%
u u
UH O
I £1
<y ..
cp >
H -P
4J ^
M
O
>
c
M cu
to >
i« <v
G ft
G co
rH rH CU G G
I o m H H
g O U X\ £
d) •• V P P
p «-»....
■H — G
.. — -H G
a> is -h
a> G 6 *
G «J — e
* ft -'
ft i +»
i u G co
G cu o cu
c c >^ n
cu cu to -h
g +J rH CO
I «»••••
_ T3 .H — ~
CD G rH
G « I g
•0 g ft-H
ft g CO IT)
ft I O -rl g
10 CD O rH ~
•H 0) •• •• —
rH H > > «•
» +J +J .p
CO
a> a; 0 ft c
C <d G co o
HJ H 0) -H -H
ft+J g rH P
4j i — - — - — m
in tj — y
■HO)' G
rH P &>
ft O CO -H
i at a> uh
P rH G G
■H 0) Ifl O
G co ft O
M -H
C
•H &»
A C
P -H
•o
S -H
0 n
c u
0)
^ >
CU 0
M
(0
P n
O ITJ
a)
rH CU
CU ?
(0
■o
CU 0
A A
V V ~
a) •
CU g UH
CO rH
0 CU tt>
n) X! to ~
O En P —
CU >— -H
„
J3 ~
0)
CU CO
iH
*_
- • a »
0
*-*
J CU i0 0
&
u
I g n -o
+J id w C
•H
<D
O U 1 -H
n
UH
11 IH ID J
*J
CU
4H
rH 1 0) 1
tO
4-1
s
CU CU fc T3
CU
IH
£1
CO CU P CU
M
3
H -P
cS
X)
0
■H
10 P CU o
•h x; <i>
0
> 10 P rH
•H
V
P
1
(V
cu UH c to
•H
4->
0)
rH 0 U 1
C
<l)
M
XJ 3 »H
•H
V)
-P
•H T) P 0
(0 10 0) UH
CO CU M 1
iH
2
CU
CU P CO
CU
a
C
O 01 T3 10
JJ
CU
*
O d rH -rl
4H
e
ft
fl-ri 3H
It)
T3
0 i0
CU H > ••
CU
C
S <" _
a>
c
CU
.0 c -o 5>
g
(0
to
u a) c e
3
ft
UH P 10 10
iH
i
CO VH
<4H
X)
UH
H -rl CU UH
0)
1
c
rH
3 rH g 1
G
CU
CU
V
O 1 10 CU
<0
CU
CO
to
ft n cu
a
u
CO CO UH u
i
V
T3
CU -H 1 -P
T)
<+H
c
>!H li-
CU
iH
CU
lt -H
P
?
<U
to
to
g i0 to T3
iO 0
O
(1)
fl
co co A ^
rH
+J
TJ
•H * -P
CU
<u
C
Xi P C cu
to
g
CU
e-i -H o e
HH
CO
UH
>
a>
... ... .► CU
P
■d
T)
Section 7.4 The Program 151
I
ft
B
n
p <« a» a> i
(0 o -p > 0
to c
cd p -h *o — a»
a w in o BE
(0 -H ,C 0)
ftrH CD P P AJ
CO) -H CO
a a) i« a i rtt
to 43 o. to e
■h +j a> a i
^ ft ,g c
V (0 Eh CO
V M •<* -* OP
45 (0 rH -HP
p g p ~ 3
CD <U ,C P M <w
CD ,* P -rl ~ d)H B
CO -H — ft CD CD
t— I <U CO O to P
>, > a> a) -h ~
cucoroxc a» — ~ i — »
«t?^'H * ~- PftC —
G <W ft C 3 -H CD —
P-HT3 I O OrHCO —
O 43 rH 10 Tj -rl* llfl 0«
cd P 3 -h cd V U X ,C ft
rH O .C P OCO CU-dO-H
CD tO > E-l O C-H I M rH
coat a) 0i-i +j-hi3X3
O P rH «H « 9 A G
a) t3 h • at ii OPcu'd
4* ~ CO ~ rH CD CD — g G
p 43 cd •• ~ cd ft p, •• o
cu-h0(Opo cd p ^ p ft cd
>! 43 £3 M - Tj rHI CD •• -H CO 0)
<d U CD »4H fi I g U ^rH-H-^
ggl^.H ft CD ~ O XI
g CD £3 £ O P ~ £3 CO D CD
OCDgCD O I P -rl ~ CJl -H TJ £3 P
p p CD M ~ -H CD II -H-H ~ £3 CD 3
CO P P — P CD £ CD <N 3 COOgO
•OJ^tO O U OCD^-P O gOCD
•H 10 >, CD ~ ~ CD P T> M rH CO TJ CD CD TJ X
fl m^ • »h co fi p 43 cd C -PcofiCD
> P > > CD a g -H * •• M -H -H — flj ••
POCDOOCQ H CD S CO «4 2 g
CO TJ 43 M T3 Tl I X P IP -P ~ — rH ~ 0) rH * ~
2CP0GCH -H -H CD 10 (3 -H-HgrHOft
•n -H IH -H -H O g I 41 'H H O - C G >> (tf -H
£ *W 3 3 <+H I CD M rH -H u-t p O P gOgrH
CD OCDIII P > P rf (3 ~ -rl -H — I O fi Ofl
> CD O rH CD CD O -H^l^ £3 p ft J*, M 3 M
43 £3 £3 flJ rH g CD P » CD ft -rl O O (3 <4H <4H 4H 43
&> p o o -h 43 m in -h ftio.- c o m- - +j
fi-rlplflC CD CO 0) >, M « 3 H •• P, IH (Oh
•H <+-l P t! C P •• CO £ (OPCDg M 4-1 I -H ft CD ft 3
A O A C 0) O I CD CD I >« I CD I ClHrH-HP-HO
P O <0 CO CD CD CD CM OgCP P -- rH ^H H XI H 3 H IH
P COrHfl 03 -HI OCD-rlfi <4-l ~ CD-- (fltt) A U X)
1* M P *4H CD CD ffl 3 X CD M P rH O K) U > "« glOP CD
M <fi O r-t tOft O — -H 01 a-H X) <M "rH CDft •• 10 <« X <« Tj
flJ ft CD CD £3 •• I g ~ g 3 CD HO TJHOCDOG
D H ffl O T) ~ I 10 >C0 IO>C-H I CD
GCDCDP CDCD — O > CO g — P O ft»H OCDMhPPPW
co jC to -h co g p C o oi i M TJ* Oi t) m ^ fl S fl) -
P -HiDO CDI THI O O 'H C» PC G~ gog
CD CDM MCD CCD CO-HTSrH -HO l-H -H 0*hCD^d
^ to^ O t7><nH (OC -HOtofift U-O ><0 SllPOftOC
E-«CDP<WCICD ftfl > h <tJ -H I IC Og I 0<4-l>,>4-ICD
0) -HCDtO Ift I ft XJ S P CD -H T3 •• CDftCD P g
0 fc CD TJ CD •• CD •• CD H CD > C CD -H t-l •- •• •- ••
•CWOO-HH> CD> CD>>>C Ml -H> HHO"---
M C <W C M P P MP hppp-H PCD SO P-Qtfl
010 OM-^ p_p^ I ^CD I t) - -
rCOlO— CD-O P MCDC U
*>,OCM M iH T3PCD-HTJOO
PfHOCDOOCD O O 0 O * M£ Oiw-O
^CDt3O^03> > (0 X3 P 43
C>C-HCDP^(0 (0 iw PO* -OPft
OCD-H?MCD-rH rH CD CDPfiGCDO
QrH>p(0g^4-l »w -O gCD 0CD gO
IH^-CH <4H •• H 01 IH 01 UH
..* .,...,.. d) , qj q) >_, ci) •— ' CD — ^ CD •
• « aw ■» •• ■« rQ ir^ frj rr^ »Q frj
152 THE TREE EXAMPLE Chapter 7
<rt ~ — fi ft 10 0 -H
v ~ * o i ** «
4J - C -H 2 S -H O
m (o * -p fi , i ft
03 -H 2*2
p, -h «h a g 0 c p
£ 5 ~ ft -cog o
P G h
jj .. o jj nJ fi "O =
o 00- i 2 S £5
+J O 0 o | « -P
•H P P O g 0> O C
C+j 0) G O +J<1)
r< fl C U C fl -C -HI G P
+5 C o p -h o p .c 0 ?> S
0 w p 3 0 *o »w o p <o
> J «j o 0 0 — os-P a co
1 ^~. CCUPO^»'-~ >iO did)
U g p to t-t • ~ -h to .22
3— 3-HOO) 0) H •» O EG
+J G O »> .C ft *d 2 o sm
-w+J o « p >> >, o a f „, 2
o * moag p 2 o
&
0)0 0)0) 0) o — — C C d _
cu in p g p o c >^r- 0 •• c ^
•H«J13 P <« 0 O -n C ^ ^ * S * , , *J
C -H P O tO «TJ <W X _ * -P -H
P tO 0 10 I - P B = p .* fc £ S ?
0) (fl -3 -rl (O-drHrHrH Pi "fl S E 1 ^ V m
<3o-HP |CpCfl> I PEh HJ P P
ocu p 0 o tn >> = m r4 a \ a 00
~ .- 7 .C 3 rH P -H <2 \ +JJQWC ftv; «
me- toc+jop<w — johs ■Vft-P
O)5>*-H0OP X ~ t* = II
ni+3pMtotoaje!<o * " -K tt - § 8 £
lH4-l~COiH0,l3"P P OWC — (UXflJ
p 0 \ r-l O PtO O CrHPq-H -H -H g ft
W\ I fi^H (J>- C P -» ^ •HOW P C lltQ
I w gSPrHC O * ~ — ^ Pll O « « -d
to Siw p-sopifltu crto pto ototo^-3 p n^5
P HH
14_| t) V
(0 •• tO 3 CU CD M-l
'U U* ^J Wi -f Wl W W< V< ^- -»— f-l ^- w
H Cffl>Q PQJ O0> -H3!Dl<w to POPS
1=0)3=0)= (OP OP rHOOPI -H *g =
CQ)P"C C — a) 10) 3 •• •• 0> 3 «3 0)
P 0)PP 0=0 — P rjP '°^^2V «, a
S O ge<= -HO) •H-'*" <0-- ^ 31 4) >%
O—O I >pprHP P 0) ' O &, O 10 P'*'
TJ—P TjaOIOlUPtJ 0 0* P* PTJJgtO >i I
a * cScoppp a>o? —is ccw/o) p g
•HPTJ JoZtfC-HC PPO -0)0 rO-HtO^O I «
SOC g nJ 0)J0) -P i "O fi "O 'd ? o— g P
100 gPPg g IrHC PPC CI-OCOP— 0) -H
0PIO OP0>0)3l03 Z <H -ri 0*-H S0CCft— P I
«*«-' u m>j n u v u 00> o > t) « « o^+j -h p
n i-P(do>ooo CPi p*i 0ptop i g
JJ4J4J 0) 1)1 S d! fl Q 1) 110 I+J0 MP — PC 0 O
i O O 0 = = =••= : • • P 0 0 P O 0 — 3&i 0 0)
00 p — — — p.cp 0OP M_t!-flP fl 81
■a^fc +j>— flpptopp-H-dOP p>»
o— *» +)**o>** ociia *p
45 — t3 co p (o^i^^^- 1
±i a u W 0"0 -r\ ±) Pt3
0)P(0 <<J C+JCCPC430-O 5*2
gO)--- > S00300PgC ><0
IUH 4-1 M-ICOlOd-ltOtO <H<fl VW"
4)^, 0) O)^^^^0^^^',*0^-' 0>
<0 tj '0'0«-,0 «o +j
The Program 153
0
0
rH
rH
,_^
u
1
•rl
•H
P
rH
9)
TJ
H
-d
a)
CO
rH
c
i
Pi
•rl
X!
o
0)
1
0
co c
1
1
T3
M 0
ai
01
»
0
0)
(0 -H
p
s
CO
C
Pi
e p
(V
id
Pi
■H
1 -rl
i-t
c
01
rH
0) CO
01
Of
CO
£
T5
W 0
TJ
M
CO
P
(0
3 ft
1
d
0)
0
ai
cy
0
>H
rl
e p
(0
CO
s
0
0
S
3
4-1
=*t
= 0
0
0
E
p p
e
e
TJ
rH
P
0
rH
01
0)
0 c
■H
£)
0)
OS -H
01 ~
■p =
01
e
=
rH
43
CO
a) a>
01 •
«
0)
1
M <d
rH 0)
c
TJ
5
tj
0)
(0 0
01 0>
0)
0
CO
CO
s c
Q U
a
a
55
rl
3
E
E p
B
0)
0
(0
CO
p
e
Q) -H
a) a>
0)
•H
CO
c
-O X!
n a
TJ
.C
T3
E
w
>
0 P
0 P
0
P
0
:
p
a
a
a
Pi
CO
•• ^3
.. m
rl
TJ
T3
P
0
0
0
rH
* tH
*
UH
c
«
P ?
■P P
p
P
o
CO
co 3
CO
rH
to
CO
— -
£i
•H 0>
•H 0
■H
0)
■H
•H
!>,
rH Q)
rH
rH
A
rH
£
0)
0)
iC H
IT] (1)
«
tT5
IT]
P
■d
*
--»
1 P
1 -o
rH
1
0
>
(9
CO 0
CO
CO
0
c
B
■—•
0
ft (U
ft G
ft 5
ft p
0
>
•d
J*.c
K
>»
0)
J*
rl
0
c
■p -p
P CO
1 -H
P
1
Pi
p
1
■d
H
0)
u
£
4H
3
•rH
e >.
e a
e
a]
a
■H
*— .
c
0
01
0
■H
0) IT]
0) 4->
0)
CO
£
•t
fC
T3
c
"3
s
Oi —
P rH
p
p
0)
p
u
0
p
C
•H
C
-d p
•H ft
•H CO
•H
«d
•rl
■d
CO
■H
rH
•H
CO
o c —
1 CO
1 o
1
■H
1
id
c
c
S
|
2e
■d
c o> ^
<D -H
CO -H
0)
>
CO
•H
P
0
— M '-
a> tj
CO rH
0)
C
CO TJ
s
0)
0)
0)
c
10 ~>
m a'
U ft
U
u
rl
TJ
O1
■d
5i
T3
P ft X!
P 06
P W
p
a,
P
<
0)
M
0
0
0 •• ^co
* =
* =
*
c
*
TJ
0
e
c
>
P
PJ
p
o
0 — P CO
U W ft pj SH
0
CO
0)
CO
C
0
1 TJ 11 CI <rl
ft
ft
ft
ft
*-.
0>
^-~
0)
*-*
u
-^
p o v u a)
>>
£
K
>*
tj
&
■d
rH
A
T3
£
i
*
C C £1 10 rH
P
p
p
p
TJ
rH
co
0
0)
CO
0
00
0)
CO
0) ~ ft ..
1
1
rH
■ H
0)
Pi
-Q
0)
Pi
-—
cu
-d
Oi
n -d
B
e
e
e
•H
£
in
id
rl
0)
Sh
o
-~~
rl
<d C — * S
V
co
0)
CO
£
u
4H
0)
rH
UH
Oi
P
UH
Pi
0)
IH
ft 4) P P 0
p
P
p
p
0
1
0)
e
|
01
P
Oi
OJ
T3
0)
i to c o *d
•rl
•rl
•H
■H
TJ
M
(0
P
Sh
01
rH
rl
CO
0
Sh
0)<- 01 O C
1
|
1
1
> TJ
c
01
rH
CO
X
Pi
>i Sh rH -H
P
P
P
P
CO
(0
0)
CO
Oi
■d
(0
iO P 10 * >
3
3
3
3
G
»
rl
s
TJ
5
a
*
S
e c ft
0
0
0
0
0
0
0
i
P
0
i oi cr tj
V
CO
0)
CO
0)
0)
XI
0)
0)
T3
0)
0)
TJ
0)
0
TJ
<u u p p c
ft
ft
a
ft
CO
■d
Pi
CO
T3
Pi
CO
TJ
c
CO
0
c
M 10 0 CO CO
rs
K
K
I*
3
0
•H
3
0
•H
3
0
•H
2
r^
•H
3 ft C co co
p
1
p
p
P
i
a
s
i
Pi
s
0
5
C
*
0
e
*
£
or
-0
■0
■d
■d
TJ
T3
TJ
TJ
TJ
T3
D"d
I4H
-d
TJ
■d
TJ
PS
G
C
c
C
Pi
a
C
C
c
p
Pi
C P -H
•0
ITJ
aj
id
3
0)
0)
3
0)
0)
2
CO
Oi
rJ
0)
0)
rJ co --
UH
no
CO
4-<
CO
CO
UH
CO
DO
<4H
CO
DO
<4H r-i
>
>
>
>
0)
0)
01
CO
CO —
p
P
p
p
TJ
T3
TJ
■d
TJ
154 THE TREE EXAMPLE Chapter 7
o>
9) H - IW
■d 0) >, >W
rd XI -O 3
O • O -P
h Ji O XI co
•O -P
91 O <U TJ
XI O <D .3 c
!-t -P 10
e T3 -H
CO 0) <4-l tT> >^
5 rH C T3
S -H P -HO
ft <0 -P XI
P B 3 3
■H O -P ft 0)
o 6 3
H OP
O P o o
U U 4-1 (1)
O 0) i+j 4_)
nj fc -d os
e n a) ft
o en v g
<o o c ho
i0 2 O
CO P M CO
+J 0> id ■ M 0)
•h g 3 =
0*0 0) CO +J •
<u p c p 0 p
O rO CO O •> rH
C TJ >> 'riH 3
•H tt) - CO • > -H CO
wh iii >, a» n v
•H rH = T> H H ~
ft -H (I) O ft CO —
• 6 <H J) XJ -HO) —
•won a> xi >>
0)O<UP — »W 3 O 4-> TJ
N P = >*, O P O O
■H CO (0 <0 H fi X>
O-H H J) O-PCO u
g <03 J3htJ iw D
<1> <1> ft -P 3 rH H -P
Eh ti M» O a> C — •
•HCOdJ ■Od)X! fc Cna)
«w *w >« o U • OS
O Art X) PT3C fc rH
CO g cdttJ-HlOQ)— ftlO
g-HC .C 0)3— — >
0XS-H3 o+JC+Jp---
•h-p^o o a> co g a>
•H 4H O *- N P -rH G CO H O^
CO-HC -H (OC «H->
•h +> a> «h o « "O a> > . --»
'mp-h.c in Eh id u tr « a)
OJCOCS N « H «1 O - ^IH 3
X><l)-H .HgC0rH ~+JrH
n uh p o i (u ai io
a> a; co goc<uc!SOPco>
£ mi) h oipoxj-h i-ioa)— -
■PXJ-H g C lUHH
P CO «W O C P > - —
CO -H O O CO <0 rH —
•HW^Tl HrH-HOS—H
« P 01 CJ CO O
a>o T3 loaiopai-P —
Mfe-Ptf g CO O -H fc <D '
ttJW^O 4H D rH rH
K CQ ft rH a> = frjOi —
■O 4H C 45
— H (0 P
Section 7.4 The Program 155
7.5 Problem Set #7
Questions
1. Write a function which puts up a menu with five choices: "Truth," "False-
hood," "Confusion," "Panic," and "Mega-panic!." Clicking on "Truth"
returns the string "Truth;" clicking on "Falsehood" also returns the string
"Truth;" clicking on "Confusion" randomly returns one of the strings
"Truth" or "Falsehood;" clicking on "Panic" enters the Fep; clicking on
"Mega-panic!" enters the Fep and does a cold boot.
2. Use the tree code to display flavor component trees. Ignore "included-
flavors" if you like.
3. You may have noticed that in the editor (and in some other contexts), put-
ting the mouse in the top-right or bottom-right corners causes the mouse
blinker to change to a squat vertical arrow, and bumping it against the top or
bottom edge of the window causes scrolling. This behavior is called "flashy-
scrolling," and is controlled by the flavor tv:flashy-scrolling-mixin. Add this
mixin to the tree window and write the necessary methods so that bumping
the mouse against the top-right corner will "scroll," i.e., make the parent of
the current root be the root.
4. There's an awful lot of duplication in the graph and tree examples. The right
thing to do would have been to take out the common portions, and build
graph and tree on top of them. Re-implement graph and tree in that fashion.
(It sounds like a lot of work, but I'll bet it won't take more than an hour.)
156 THE TREE EXAMPLE Chapter 7
Hints
Your function need simply call tvrmenu-choose with an argument of an
appropriate item list. See section 14.3 of volume 7 for a description of the
possible forms for menu items. The last three menu items will have to be of
the "general list" form.
Constructing a tree which corresponds exactly to the ordered list of flavors
used for building combined methods is tricky because of "included-flavors."
But it's not hard to make a tree showing the regular components. Given a
flavor's name in the form of a symbol, ( si : f lavor-depends-on (get
flavor-name 'si: flavor)) returns a list of the components. Give the
node corresponding to a flavor one child for each of the flavor's components,
and recurse on them. To make the tree fit on the screen you'll probably want
to switch to a smaller font, and decrease the values of *vertical-spacing* and
* horizontal -spacing* .
The best way to find out about tv:flashy-scrolling-mixin is to read the source
code. There isn't very much of it. You'll have to write methods to handle
the messages : scroll-more-above and : scroll-more-below (which
should return t when it is meaningful to scroll in the given direction and
nil when it is not), the message :y-scroll-to, which should do the
actual scrolling, the message :scroll-bar-p, which allows or inhibits
scrolling, and the message : handle-mouse-scroll. I suggest that
: scroll-more-above return nil when *root* is eq to *the-real-root*,
and t otherwise; that : scroll-more-below always return nil; that
:y-scroll-to call the function mouse-make-parent-root on *root* and
self; that :scroll-bar-p always return t; and that : handle -mouse -
scroll always return nil. (The : handle-mouse-scroll method isn't
directly related to flashy-scrolling, but because our :scroll-bar-p
method returns t, the system is going to think that in addition to flashy-
scrolling, we have a standard scroll bar in the left margin. So every time the
mouse bumps against the left margin, our window will be sent that message.
The method needn't do anything, but it had better be defined or we'll get an
error. This may sound like a modularity problem, and in fact it is. It
appears that someone assumed only windows with scroll bars would use flashy
scrolling.) Don't forget to make another tree-frame, since mixing in
tv:flashy-scrolling-mixin will be an incompatible change to tree-window.
The basic part might define flavors node, node-window, node-pane and node-
frame, and methods for them. Graph would then build graph-node on top of
node, and graph-pane and graph-frame on top of node-pane and node-frame.
Tree would similarly build tree-node on top of node and tree-pane and tree-
Section 7.5 Problem Set #7 157
frame on top of node-pane and node-frame.
158 THE TREE EXAMPLE Chapter 7
Solutions
1. (defun silly-menu ( )
( tv : menu-choose
' ( "Truth"
( " Fa 1 s ehood " " Truth " )
("Confusion" :eval (nth (random 2)
'("Falsehood" "Truth")))
("Panic" :funcall sirhalt)
( "Mega-panic I " :eval (sirhalt (format nil "b~%" ) ) ) ) ) )
2. Here is a crude first pass at making flavor component trees. It ignores
included flavors as well as duplication of components.
(setq *vertical-spacing# 5)
(setq *horizontal-spacing* 10)
(send *tree-window* : set-font-map '( fonts : tiny ) )
(defun show-flavor-tree (flavor-name)
( start-new-tree )
(send *the-real-root* : set-label
(format nil' "~S" flavor-name))
(show-flavor-subtree flavor-name *the-real-root* )
(send *tree-window* : refresh ) )
(defun show-flavor-subtree (flavor-name parent)
(loop for component- flavor
in (reverse ( si : f lavor-depends-on
(get flavor-name 'si : flavor )) )
for node = (make-instance 'node : label
(format nil "~S" component-flavor))
do (send parent :add-child node)
(show-flavor-subtree component- flavor node)))
We can avoid duplication by keeping a list of all the flavors seen so far, and
using a new flavor only if it isn't already on the list. (Modifications in upper
case.) If you want try accounting for all the vagaries of "included-flavors,"
look at the function si:compose-flavor-inclusion
(DEFVAR *SEEN-LIST*)
Section 7.5 Problem Set #7 159
(defun show-flavor-tree (flavor-name)
( start-new-tree )
(send *the-real-root* : set-label
(format nil "-S" flavor-name))
(LET ( (*SEEN-LIST* (LIST FLAVOR-NAME)))
(show-flavor-subtree flavor-name *the-real-root* ) )
(REVERSE-ALL-DESCENDANTS *THE-REAL-ROOT* )
(send *tree-window* : refresh))
(defun show-flavor-subtree (flavor-name parent)
(loop for component-flavor
in ( si :f lavor-depends-on
(get flavor-name 'si: flavor))
UNLESS (MEMQ COMPONENT- FLAVOR *SEEN-LIST*)
DO (LET ((node (make-instance 'node rlabel
(format nil "~S"
component-flavor) ) ) )
(PUSH COMPONENT- FLAVOR *SEEN-LIST* )
(send parent :add-child node)
(show-flavor-subtree component-flavor node))))
This function will make nonsense of any existing space requirement info.
Fortunately, we can be sure there won't be any, since these nodes were just
created and have never been displayed. (We couldn't just reverse the chil-
dren as we get to them the way we did above because then we'd keep the
wrong node when a flavor appears more than once.)
(DEFUN REVERSE-ALL-DESCENDANTS (NODE)
(SEND NODE : EVAL-INSIDE-YOURSELF
'(SETQ CHILDREN (NREVERSE CHILDREN)))
(LOOP FOR CHILD IN (SEND NODE : CHILDREN)
DO (REVERSE-ALL-DESCENDANTS CHILD)))
3. (def flavor tree-window ()
( tv:f lashy-scrolling-mixin ; this was added
tv : process-mixin
tv: basic-mouse- sensitive-items
tv: window)
( :default-init-plist
: process ' (tree-window- top-level-function)
: item-type-alist *tree-item-type-alist*
:blinker-p nil
: font-map ' (fonts :hl12i )) )
160 THE TREE EXAMPLE Chapter 7
(def method (tree-window : scroll-more-above) nil
(neq *root* *the-real-root* ) )
(def method (tree-window : scroll-more-below) nil
nil)
(def method (tree-window :y-scroll-to) (ignore ignore
(mouse-make-parent-root *root* self))
(def method (tree-window : scroll-bar-p) nil
t)
(def method (tree-window : handle-mouse-scroll ) nil
nil)
4. See the file "book: tape; graph &tree.lisp'
Chapter 8
RANDOM USEFUL TOPICS: RESOURCES & SYSTEMS
8.1 Resources
The documentation on resources (chapter 1 8 of volume 8) is not bad. What follows
is primarily a condensation of it.
In cases where a program uses and then discards large objects at a high rate, it can
be worthwhile to do the storage management manually, rather than relying on the
garbage collector to eventually clean up. The resource facility provides a simple
way to do so, and is widely used throughout the system software. The Chaosnet
code allocates and frees packets, which are moderately large, at a very high rate.
The window system allocates and frees certain kinds of windows, which are very
large, moderately often. Both use resources.
For each resource defined, there is a free list kept of suitable objects. Allocating a
resource involves checking the list of available objects and returning one if there are
any. If not, a new one is created and returned. Deallocating a. resource involves
returning a previously allocated object to the free list. So the storage space occu-
pied by a deallocated object is not really freed in the sense that the garbage collec-
tor reclaims free space; it does not become available to be used as part of any
162 RANDOM USEFUL TOPICS: RESOURCES & SYSTEMS Chapter 8
newly created lisp object. The original object continues to occupy the storage
space, but may itself be reused through being allocated again.
The four functions and macros which together compose the resource facility are
defresource, for defining a new resource, allocate-resource, for allocating an object
from a resource, deallocate-resource, for freeing an allocated object, and using-
resource, which temporarily allocates an object and then deallocates it.
A call to defresource looks like this:
(defresource name parameters
keyword value
keyword value
... )
name should be a symbol, which will be the name of the resource, and which will
get a defresource property of the data object representing the resource.
parameters is a (possibly empty) lambda-list of pseudo-arguments which will re-
strict the objects considered suitable to some subset of those on the free list. For
instance, a resource of 2D arrays might have two parameters, the number of rows
and the number of columns. When allocating an object from this resource, you
could specify how many rows and columns it should have. The free list would be
filtered for arrays with the requested dimensions — if all the arrays on the free list
had the wrong dimensions, a new one would be created.
There are seven possible keyword options. Only one is required, the constructor
option.
constructor
The value is either a form or the name of a function. It is responsible for
making an object, and is used when someone tries to allocate an object and
there are no suitable free ones. If value is a function, it is given the internal
data structure for the resource and any supplied parameters as its argu-
ments. If it is a form, it may access the parameters as variables.
initializer
Value is again either a form or the name of a function. If an initializer is
provided, it will be called on each object as it is about to be allocated,
whether the object was just created or is being reused. If value is a func-
tion, its arguments will be the resource data structure, the allocated object,
and the supplied parameters. If value is a form, it may reference the
parameters as variables, and also the allocated object, via the variable
object.
Section 8.1 Resources 163
rchecker
A form or the name of a function. The job of the checker is to determine
whether a given existing object is safe to allocate. If no checker is specified,
the default action is to consider an object safe if it is not currently in use
(i.e., has not been allocated without being deallocated). If you specify a
checker it will be used instead. A function here will be passed arguments of
the resource data structure, the existing object being considered for alloca-
tion, the value of in-use-p for that object, and the supplied parameters. A
form may reference the parameters as variables; the object under considera-
tion, as object; and in-use-p. As you can see, the free list for a resource is a
somewhat hypothetical object. When you ask to allocate an object, all of
the existing objects are initially eligible. The default checker creates the
functional equivalent of a free list by approving only those objects which are
in fact free, but you needn't have this behavior. Supplying your own
checker will change it. If, for instance, your checker always returned t, a
given object could be simultaneously in use in any number of places, because
it would always be considered safe for allocation, regardless of whether the
previous allocater had deallocated it. And if your checker always returned
nil, no object would be reused; every allocation request would result in the
construction of a new object.
matcher
A form or the name of a function. If no matcher is specified, an object is
considered to satisfy the supplied parameters if they are equal to the param-
eters supplied at the time the object was constructed. If you specify a
matcher, it will be used instead. A function here will be passed arguments
of the resource data structure, the existing object being considered for allo-
cation, and the supplied parameters. A form may reference the parameters
as variables, and the object under consideration, as object.
rfinder
A form or the name of a function. If this option is provided, the usual
method for finding an object to allocate will not be used. The finder will
instead be expected to somehow come up with an object. The checker,
matcher, and constructor will not be called, unless the finder does so expli-
citly. A form or function specified here will see the same arguments as the
constructor would.
:initial-copies
Value is a number, defaulting to 0. The specified number of objects will be
constructed when the defresource is evaluated, thus creating an initial free
pool of unallocated objects. If a resource has parameters and one or more
initial copies are specified, the parameters must all be optional; the initial
copies will have the default values of the parameters.
164 RANDOM USEFUL TOPICS: RESOURCES & SYSTEMS Chapter
:free-list-size
Value is a number, defaulting to 20. It specifies the size of the array used
to contain all the objects. (If the number of objects ever exceeds this size,
the array is automatically replaced with a larger one.) :free-list-size is a
misnomer, since the array contains both the free objects and the ones that
are in use. As the earlier discussion of the : checker option pointed out,
there isn't really any free list at all.
For all of the options which accept a form or the name of a function, if a form is
supplied it is compiled during the evaluation of defresource, and put on the property
list of the name of the resource.
allocate-resource resource -name &rest parameters
An object is allocated from the specified resource, matching the given
parameters. The exact procedure followed depends on which options were
supplied to defresource for this resource. If there is a finder, it is called and
whatever it returns is used. Otherwise the set of existing objects is searched
for one which satisfies both the checker (by default, is not in use), and the
matcher (by default, was constructed with parameters equal to the current
ones) . If none are found, the constructor is called to create one. Finally, no
matter which of the three routes yields an object, the initializer (if any) is
called on it, and the object is marked as being in use.
deal locate -resource resource -name object
The object is conceptually returned to the specified resource's free-list, i.e.,
its in-use marker is turned off.
using-resource {variable resource parameters ...) body ...
This macro, which calls allocate-resource and deallocate-resource, is pre-
ferred over calling those two functions directly. The body forms are
evaluated inside a context where variable is bound to an object allocated
from resource with the specified parameters. The object is deallocated at
the end. An unwind-protect is used to guarantee that the object is deallo-
cated before using-resource is exited.
Now an example. We define a resource of 2D arrays, with parameters for the
number of rows and columns, which default to 100 each. A matcher is provided
which accepts any array whose dimensions are at least as great as the given param-
eters. (The default matcher would require that the dimensions be exactly the same,
meaning that we would very rarely reuse an object.) And an initializer fills the
array with 0's.
Section 8.1 Resources 165
(defresource sloppy-2D-array (^optional (rows 100) (columns 100))
: constructor (make-array (list rows columns))
: matcher (and (^ (array-dimension-n 1 object) rows)
(^ (array-dimension-n 2 object) columns))
: initializer (fillarray object '(0)))
And to use our resource:
(defun do-complex-computation (x y)
(using-resource (temp-array sloppy-2D-array x y)
(setf (aref temp-array i j) ( calculate-value i j))
.. .))
There are several other built-in functions for dealing with resources.
deallocate-whole-resource resource-name
Deallocates all allocated objects of the specified resource. Use with caution,
as it can lead to allocation of objects which somebody else is still using.
clear-resource resource-name
Makes the resource forget about all its existing objects. Future calls to
allocate-resource will result in creation of new objects. Useful if the
resource has been changed so that the old objects are no longer usable, or if
some of the old objects have been damaged.
map-resource resource-name function &rest args
Applies function to each object known about by the resource. The argu-
ments to function will be: the object, the value of in-use-p for the object,
resource-name, and any additional arguments as specified by args.
8.2 Systems
The system facility provides a mechanism for keeping track of multiple files which
together make up a single program. The group of files taken together is defined to
be a system with the defsystem macro. Loading and/or compiling some or all of
the files in a system is accomplished via the make-system function.
The system facility is far more complicated than the resource facility, and not
166 RANDOM USEFUL TOPICS: RESOURCES & SYSTEMS Chapter 8
necessarily very well-designed ("hairier than a breadbox," in the local parlance). It
is supposed to be completely redone in a forthcoming Symbolics software release, so
it's probably not worth your trouble to learn the more difficult features just now.
We'll talk about some of the basic features; just enough for you to be able to use
systems for your own programs.
The documentation on systems is Part II of volume 4, and is also quite extensive
but not necessarily very well-designed, so this portion of the notes will follow the
manual less closely than the resource section did.
Here is a typical call to defsystem:
(def system bar
( : pathname-default "q : >george> " )
(: module reader-macros "rdmac")
(: module other-macros "macro")
(: module main-program "main" "commands")
( :compile-load reader-macros)
( :compile-load other-macros (:fasload reader-macros))
( :compile-load main-program
(rfasload reader-macros other-macros)))
All four files involved are in the "george" directory on host "q." They are divided
into three modules: reader-macros and other-macros, which consist of one file
each, and main-program, which contains two files. The reason for this particular
division is that it reflects the dependencies among the files, as specified in the
: compile -load clauses. Each :compile-load clause states that the files in
the specified module should be compiled if necessary (if the newest source file is
more recent than the newest object file), and then loaded if necessary (if the newest
object file is more recent than the last one to have been loaded), possibly subject to
certain dependencies.
The reader-macros module (file "rdmac") does not depend on any other modules.
The other-macros module (file "macro"), on the other hand, does depend on
reader-macros. The (rfasload reader-macros) dependency for other-
macros means that the file(s) in reader-macros have to be loaded before those in
other-macros may be compiled or loaded. The reason is presumably that the file(s)
in other-macros contain calls to macros defined in reader-macros, which must be
loaded for the calling functions in other-macros to compile correctly. The files in
main-program are dependent on both the other two modules. Presumably they con-
tain calls to macros defined in both reader-macros and other-macros, and so require
that both modules be loaded before main-program may be compiled.
Section 8.2 Systems 167
Once the system "bar" has been defined in this manner, it can be loaded and/or
compiled with make-system, (make-system 'bar) will load any files that need
to be loaded, without doing any compilation. (make-system 'bar : com-
pile) will first do any compilations that are needed, and then do any loading that
is necessary. By default, make-system asks for confirmation before actually doing
any compiling or loading.
One bit of terminology: the operation of compiling or loading an individual file is
called a transformation. So a defsystem could be seen as defining what transforma-
tions a system is composed of, and a make-system as a command to see which of
the transformations are necessary, and to carry them out.
Now we'll go into some more of the various options for defsystem and make-system.
Many will be skipped entirely.
For defsystem:
We've already seen rpathname-default, : module, and :compile-load.
:component-systems
The mechanism for including other defined systems as parts of this one.
What follows the keyword is a list of systems. When a make-system is done
on this system, it will also be done on each of the component systems. By
default, the transformations for this system will be performed before the
transformations for each of the component systems. (Yes, that seems wrong
to me, too.) But the default ordering can be overridden. If some of the
local transformations depend on having the component systems done first,
you can use (: do -components nil). Put it at any position in the body
of the defsystem, and the transformations of the component systems will be
performed at a time corresponding to the chosen position in relation to the
local system's : compile- load transformations.
:package
Specifies a package in which the transformations are to be performed, over-
riding any package specifications in the attribute lists of the individual files.
:patchable
Allows the system to be patched* (incrementally updated). Appropriate for
PATCH
1. noun. A temporary addition to a piece of code, usually as a quick-and-dirty remedy to an
existing BUG or MISFEATURE. A patch may or may not work, and may or may not eventu-
ally be incorporated permanently into the program.
168 RANDOM USEFUL TOPICS: RESOURCES & SYSTEMS Chapter 8
large systems which are to be distributed to many users. The latest patches
for a patchable system may be loaded with the load-patches function.
And for make-system:
(Recall that the :compile keyword must be specified for any compilations to occur.
By default, only loading is done.)
:noconfirm
Assumes a yes answer for the questions make-system would otherwise ask
before performing transformations.
:print-only
Performs no transformations at all; just prints information about which
transformations would need to be done.
:version
Loads a particular version of a patchable system. There are many different
ways to specify which version — see the documentation.
One last issue remains with respect to systems. Since make-system only works
after the corresponding defsystem has been evaluated, it's important to have a con-
venient way to get the defsystem done. Knowing what file it's in and loading that
file manually before doing a make-system for the rest is not convenient. For-
tunately, there is something better. Whenever make-system is called on an
unknown system, i.e., one for which a defsystem has not yet been done, make-
system looks in a predetermined place for a file to help it out. If there is a file
named "sys: site; system -name. system" (a logical pathname whose physical transla-
tion depends on your site), make-system will first load that file, and then try to
make the system.
The file should contain either the defsystem itself, or a call to si:set-system-source-
file, which will tell make-system what file does contain the defsystem. The two
arguments to si:set-system-source-file are the name of the system and the file where
the system definition may be found. If you're the only person likely to be using the
system, another idea would be to put the call to si:set-system-source-file in your
login init file, thus eliminating the need to put a special file in the "sys: site;"
2. verb. To fix something temporarily; to insert a patch into a piece of code. See KLUGE
AROUND.
(The Hacker's Dictionary, Guy L. Steele, Jr., et at)
Section 8.2 Systems 169
directory.
170 RANDOM USEFUL TOPICS: RESOURCES & SYSTEMS Chapter 8
8.3 Problem Set #8
Questions
1. Find out about def window -resource. It's used many times in the file "sys:
window; sysmen", which contains the code for the system menu. It's also
used in the "Smiling Face" example, "sys: examples; smile". (If you have
Release 5 documentation, you can find a description of this example, includ-
ing a few comments on def window -resource, in WINDEX). Now think of
some sort of window for which it would be useful to have a resource, define it
with defwindow-resource, and use it.
2. Look through the "sys: site;" directory. Pay close attention to the system
definition files and the logical host definition files. (Recall that system
definitions are by default found in "sys: site; sys -name. system''' and logical
host definitions in "sys: site; host -name. translations" . Note also that if the
"sys:" logical host happens to translate at your site to a UNIX system with 14
character limit on filenames, then "sys-name.system" will be translated to
"sys- name. sy" and "host -name. translations" to "host -name. Id".)
Most of the system def files will probably not directly include the defsystem,
but will have a call to si:set-system-source-file, specifying some other file as
the system source file. Track down a few of these pointers, and examine the
defsystems. (Keep in mind that if si:set-system-source-fiIe uses a logical
pathname, the logical host must be defined with fs:make-logical-pathname-
host before you can find the file.) Assure yourself that you understand how
it is that make-system can find the defsystem for a new system. (You might
want to look at the source code and see how make-system calls find-system-
named, which calls maybe-reload-system-declaration, which calls make-
system-site-file-pathname.)
Section 8.3 Problem Set #8 171
Solutions (roughly speaking)
There isn't much to say about this.
Let's consider what happens if you do (make -system 'ip-tcp) on a
machine that so far knows nothing about "ip-tcp." Since a system named
"ip-tcp" has not been defined, make-system will look in the "sys: site;" direc-
tory for a file named "ip-tcp.system". At my site, the logical pathname "sys:
site; ip-tcp.system" translates to the physical pathname "l:>sys-6> site > ip-
tcp.system". Having found that file, make-system will load it. Here's what's
in the file:
;;; -*- Mode: LISP; Package: USER; Base: 10 -*-
( f s :make-logical-pathname-host "IP-TCP" )
(si: set-system-source-file "IP-TCP" "IP-TCP: IP-TCP; SYSTEM")
The second form tells make-system where to find the defsystem for ip-tcp.
But the file is specified as a logical pathname, using host "ip-tcp," which is
not yet defined. Thus the necessity of the first form, the fs:make-logical-
pathname-host. Recall that this function, when given an unknown host, also
has a specific place to look for a file to define the logical host: "sys: site; ip-
tcp.translations". At my site again, that translates to "l:>sys-6>site>ip-
tcp.translations". So as part of the evaluation of (fs:make-logical-pathname-
host "ip-tcp"), that file will be (recursively) loaded. And here's what's in it:
;;; -*- Package: USER; Base: 10; Mode: LISP -*-
( f s : set-logical-pathname-host
"IP-TCP"
: physical-host "L"
translations '(("IP-TCP;" ">sys-6>ip-tcp>" )
("IP-TCP; PATCH;" ">sys-6>ip-tcp>patches>" ) ) )
Now things begin to bottom out. This form defines the logical host "ip-tcp,"
and specifies which directories on physical host "1" (Laphroaig, one of our
lisp machines) the logical pathnames should translate to. As the loading of
this file is completed, the call to fs:make-logical-pathname-host will also
return, and we continue on to the si:set-system-source-file. But now the logi-
cal pathname "ip-tcp: ip-tcp; system" is meaningful, and translates to
"l:>sys-6>ip-tcp> system". So make-system is now informed as to which
file contains the defsystem for ip-tcp, and proceeds to load it. Here's what's
172
RANDOM USEFUL TOPICS: RESOURCES & SYSTEMS Chapter 8
in that file:
-*- Mode: Lisp; Package: TCP; Base
DOD Internet Protocol support.
10
Symbolics' copyright notice
(def system ip-tcp
:name "IP-TCP")
:maintaining-sites :scrc)
: pathname-default " IP-TCP : IP-TCP ; " )
:patchable "IP-TCP: IP-TCP; PATCH;")
: component- systems
tcp tcp-service-paths ip-tcp-applications!
:module chaos "chaos-unc-interf ace" )
: module global "ip-global")
:module main ("ip" "ip-routing" ) )
:module ip-protocols ("icmp" "udp" "egp"))
:compile-load chaos)
:compile-load global)
:compile-load main (:fasload global))
:compile-load ip-protocols (:fasload global))
: do -components (:fasload global)))
. . . defsy stems for tcp, tcp-service-paths, and ip-tcp -applications . . .
There's all kinds of stuff at the tail end of this file, which is a perfectly legiti-
mate short-cut. We can assume that it will all be read the first time someone
does a (make-system 'ip-tcp). But the crucial part is that this file
must contain the defsystem for ip-tcp, which you can see above. Once the
loading of the file is complete, make-system will continue. It now knows how
the ip-tcp system is defined, and can proceed to load the files which compose
the system. The filenames are all specified with ip-tcp logical pathnames, but
that's okay since we've already defined the ip-tcp logical host.
This sort of bootstrapping may seem awfully baroque, but notice that the
whole mess can be transferred to some other site with a minimum of effort.
The only line of code that would need to be changed is : physical-host
"L" in the "sys: site; ip-tcp.translations" file.
Chapter 9
SIGNALING AND HANDLING CONDITIONS
9.1 Overview
The material for this chapter comes entirely from Part XI of volume 2 of the Sym-
bolics documentation.
An event is some set of circumstances a running program can detect. Some events
are classified as errors, but not all are. Division by zero is an event which is an
error. When an event occurs, the program reports that fact, and finds some user-
supplied code to execute as a result.
The reporting process is called signaling, and the user-supplied code which subse-
quently assumes control is a handler. The system software includes default
handlers for a standard set of events.
The mechanism for reporting an event relies on flavors. Each class of events
corresponds to a flavor, called a condition. Signaling is more fully described as sig-
naling a condition, which involves creating a condition object (an instance of the
appropriate flavor). When division by zero occurs and is signalled, the condition
object created is an instance of the flavor sys:divide-by-zero. The instance variables
174 SIGNALING AND HANDLING CONDITIONS Chapter 9
of the condition object will contain information about the event, including what
string to use if an error message becomes necessary.
Each handler is defined to be applicable only for a particular flavor of condition
object. It can be used only when the condition signalled is an instance of that
flavor, or one built on it. The set of conditions a handler can handle is thus deter-
mined by the flavor inheritance mechanism. Handlers have dynamic scope, so
finding a handler to invoke for a given condition involves stepping through the stack
and grabbing the first handler which is applicable to that condition.
There are several kinds of actions a handler can take. It may instruct the program
to continue past the point where the condition was signalled, possibly after correct-
ing the circumstances that led to the condition being signalled. This is called
proceeding. Or it may unwind the stack all the way to the point where the handler
was bound, flushing the pending operations. (This behavior is essentially equivalent
to what you'd get with a catch in place of the handler, and a throw — with the
correct tag — in place of the signaling of the condition.) Or it may partially
unwind the stack to some intermediate point and reexecute from there. This kind
of handler is called a restart handler.
There are three ways to customize the condition mechanism:
• Defining handlers for existing flavors of conditions which may be signalled by
the system code.
• Signaling existing flavors of conditions within your code, which may invoke the
system's default handlers or ones that you've written.
• Defining new flavors of conditions.
9.2 A Few Examples
(condition-case ()
(// a b)
( sys : divide-by-zero *inf inity* ) )
This form binds a handler for the sys:divide-by-zero condition, and evaluates ( //
a b) in that context. If the division finishes normally, its value is returned from
the condition-case. But if b turns out to be zero, the sys:divide-by-zero condition is
signalled, and our handler is invoked, which simply causes the condition-case to
return the value of the symbol * infinity*.
(def flavor block-wrong-color () (error))
Section 9.2 A Few Examples 175
(def method (block-wrong-color : report) (stream)
(format stream "The block was of the wrong color."))
This defines a new error condition. (To define a condition which is not to be
treated as an error, build it on the flavor condition rather than error.) The
: report method is required. If your program now executes (error 'block-
wrong-color), your new condition will be signalled. If there are no handlers
currently bound for this condition, a default handler will cause an entry into the
debugger and use the : report method to generate the error message. This par-
ticular error message, however, is not terribly informative. It would be nice to
know which block had the wrong color, and what color it had. Here:
(def flavor block-wrong-color (block color) (error)
: initable-instance-variables
: gettable-instance-variables )
(def method (block-wrong-color : report) (stream)
( format stream
"The block ~S was ~S, which is the wrong color."
block color) )
Now that we've added the block and color instance variables, we can do some-
thing like
(error 'block-wrong-color : block the-bad-block
: color the-bad-color )
which will initialize the instance variable as specified, and thus allow the : report
method to get at that information.
Another function which may be used to signal conditions is signal. It has the same
syntax as error, but may be used with any flavor of condition, whereas error is re-
stricted to use with error conditions, i.e., conditions built on the flavor error (which
is indirectly built on the base flavor condition, via the intermediate flavor
dbg:debugger-condition) . There are two additional differences, signal allows
handlers to proceed the condition, and error does not. (Thus error is guaranteed
never to return to its caller.) And when called on a simple condition {i.e., one not
built on dbg:debugger-condition, as the flavor error is) , signal returns nil if there are
no handlers currently bound for the condition. (This is actually more a difference
in the behavior of different flavors of condition than a difference in the behavior of
the functions signal and error, but since error can't be used with simple conditions,
it works as well to think of it as related to the function used.) If you signal any
176 SIGNALING AND HANDLING CONDITIONS Chapter 9
condition built on dbg:debugger-condition, with either signal or error, and there are
no handlers bound for that condition, you always end up in the debugger.
The function most frequently chosen for signaling is actually neither of these, but
rather ferror. It's used to signal an unproceedable error when you don't particu-
larly care which condition is utilized, (ferror eventually calls error, with a condi-
tion flavor of ferror.) It allows you to specify a format control string and argu-
ments to be used to construct the error message.
The macros check-arg and check-arg-type are also very handy for signaling an
error in case a function has been sent inappropriate arguments.
9.3 More on Handlers
The following is (I believe) a complete list of the macros provided for convenient
binding of handlers: ignore-errors, condition-case, condition-case-if, condition-bind,
condition-bind-if, condition-bind-default, condition-bind-default-if, condition-call,
and condition-call-if. We will discuss four of the nine.
ignore-errors body...
This one's easy. It binds a handler which handles absolutely every kind of
error condition (not simple conditions) and does absolutely nothing. Upon
seeing the error, the ignore-errors form returns. There will be no indication
that the error ever occurred (except, of course, that any code within the
ignore-errors following the error will not have been executed), ignore-errors
is intended to replace the older forms errset and catch-error.
condition-case (vars...) form clauses...
form is evaluated in a context with handlers bound as specified in clauses.
If form returns without signaling any conditions, the condition-case also
returns (subject to one exception — see below). If a condition is signalled,
the clauses are checked for a handler bound for that condition. If one is
found, the rest of that clause tells how to handle the condition. If a condi-
tion is signalled for which the condition-case has not bound any handlers,
the signal continues up the stack.
Each clause is a list whose car is the name of a condition flavor (or list of
condition flavors) and whose cdr is a list of forms to evaluate. If a condition
is signalled matching the flavor (s) (i.e., equal to it or built on it) specified in
a clause, the "handler" consists of executing the forms in the dynamic
environment of the condition-case, not the environment where the signal
occurred. That is, the stack is automatically unwound before the handler is
Section 9.3 More on Handlers 177
executed. As a result, the handler may not proceed the condition. While
the handler is running, the first symbol in vars will be bound to the condition
object.
As a special case, the car of the last clause may be : no-error. Then if
no condition is signalled during execution of the body, instead of returning
form's return values, the vars will be bound to those values, the mo-
error forms will be evaluated in that context, and condition-case will
return whatever they return.
This example is essentially equivalent to ( ignore-errors (do-this) ):
(condition-case ()
(do-this)
(error nil) )
And this one uses the condition object:
(condition-case (e)
(time rparse string)
(time: parse-error (format error-output
"-A, using default time instead."
e)
♦default-time*) )
condition-bind bindings body...
Condition-bind provides similar functionality to condition-case, with the
additional capability of proceeding from conditions. Each binding in the list
of bindings is a list of two elements, the name of a condition flavor (or list of
flavors), and a form which produces a handler function. The form is typi-
cally a quoted symbol, with the symbol being given a function definition
elsewhere. But the form may also be a lambda expression. The body con-
sists of any number of forms. If a condition is signalled during the evalua-
tion of the body, the bindings are searched just as with condition-case. If a
match is found, the handler function is called with one argument, the condi-
tion object. The handler runs in the dynamic environment in which the
error occurred; unlike with a condition-case, the stack is not unwound.
The handler function has three options. It may return nil to indicate that
it doesn't wish to handle the condition after all. (The search will then con-
tinue for a willing handler.) It may use throw to unwind the stack to some
outer catch. Or it may proceed the condition by returning a non-nil value.
There are several things to keep in mind if you wish to proceed a condition.
First, the condition must be of a type which is proceedable; second, the
178 SIGNALING AND HANDLING CONDITIONS Chapter 9
condition must have been signalled with signal, rather than with error; and
third, the handler should send the condition the : proceed message (with
an appropriate argument) and return whatever values the : proceed
method returns, because the : proceed method may decline to actually
proceed and return nil, in which case the handler should also return nil.
Apart from being able to proceed conditions, the other advantage of running
in the environment where the condition was signalled is that you may exam-
ine the stack. A handler might choose from among its three options accord-
ing to what it finds on the stack, or it might print some message whose con-
tents is determined by the state* of the stack. Section 63.4 ("Application:
Handlers Examining the Stack") discusses the functions which are available
for this purpose.
condition -bind -default bindings body...
Beyond the regular bound handlers, you can also define default handlers,
with condition-bind-default. The list of current default handlers is checked
only after all the bound handlers have declined to handle a condition. Thus
by setting up a default handler you can allow outer bound handlers to take
precedence over your handler, but still have your handler invoked if there
are no appropriate bound handlers. (See chapter 65, "Default Handlers and
Complex Modularity.")
9.4 Restart Handlers
There are several macros for establishing restart handlers. Here's an example
taken from the chaosnet code:
(defun connect (address contact-name
^optional (window-size default-window-size)
(timeout (* 10. 60 . ) )
STATE noun.
Condition, situation. Examples: "What's the state of your latest hack?" "It's WINNING
away." "The SYSTEM tried to read and write the disk simultaneously and got into a totally
WEDGED state."
A standard question is "What's your state?" which means "What are you doing?" or "What
are you about to do?" Typical answers might be "I'm about to GRONK OUT" or "I'm
hungry."
Another standard question is "What's the state of the world?" meaning "What's new?" or
"What's going on?"
(The Hacker's Dictionary, Guy L. Steele, Jr., et at)
Section 9.4 Restart Handlers 179
&aux conn real-address (try 0))
( error-restart
( connection-error
"Retry connection to -A at -S with longer timeout"
address contact-name)
forms... ) )
This function evaluates forms and returns the last value if successful. But if the
debugger assumes control as a result of a chaosreonnection-error condition during
the evaluation, the user will be given the opportunity to restart by typing one of the
super keys. The debugger printout will include a line that looks something like
s-A: Retry connection to SCRC at FILE 1 with longer timeout
If the user then types s-A the body of the error-restart will be executed again from
the beginning. Now the full descriptions of two of the macros that may be used to
establish restart handlers:
error-restart {condition -flavor format -string format-args. . . ) body . . .
This form establishes a restart handler for condition-flavor and then evalu-
ates the body. If the handler is not invoked, error-restart returns the values
produced by the last form in the body and the restart handler disappears.
When the restart handler is invoked, control is thrown back to the dynamic
environment inside the error-restart form and execution of the body starts
all over again, condition-flavor is either a condition or a list of conditions
that can be handled, format-string and format-args are a control string
and a list of arguments to be passed to format to construct a meaningful
description of what would happen if the user were to invoke the handler.
format args are evaluated when the handler is bound. The debugger uses
these values to create a message explaining the intent of the restart handler.
error-restart-loop (condition -flavor format -string format - args. ..) body. . .
Similar to error-restart, but with an infinite loop. If the handler is not
invoked, instead of returning the body is reexecuted. (The loop may be
exited with return.) This form is commonly used in the top-level function
for a process, with condition-flavor being (error sys:abort).
9.5 More on Proceeding
Chapter 68 ("Proceeding") is a cogent five-page discussion of what is involved in
programming proceedable errors. I recommend reading it. I will include just a few
highlights here.
180 SIGNALING AND HANDLING CONDITIONS Chapter 9
For proceeding to work, two conceptual agents must agree:
• The programmer who wrote the program that signals the condition
• The programmer who wrote the condition-bind handler that decided to proceed
from the condition, or else the user who told the debugger to proceed.
The signaller signals the condition and provides a set of alternative proceed types.
The handler chooses from among the proceed types to make execution proceed. A
proceed type is defined by giving the condition flavor a .'proceed method.
(: proceed methods are combined using the : case combination type, so that one
flavor may have any number of : proceed methods, each defining a different type.
The first argument to the method dictates which : case is to be called.)
The body of the : proceed method can do anything it wants, generally trying to
repair the state of things so that execution can proceed past the point at which the
condition was signalled. It may have side-effects on the environment, and it may
return values (which will then be returned by signal) so that the function that
called signal can try to fix things up. Its operation is invisible to the handler; the
signaller is free to divide the work between the function that calls signal and the
: proceed method as it sees fit.
Review: suppose a condition is signalled for which a handler has been bound with
condition-bind. The handler function is called with one argument, the condition
object, and it may throw to some tag, or return nil to decline to handle the condi-
tion, or try to proceed the condition. To proceed, it must first determine which
proceed types are valid for the condition object. This must be done at run-time
because condition objects can be created that do not handle all of the proceed types
for their condition flavor, via the :proceed-types init option, and because con-
dition objects created with error instead of signal will have no proceed types. The
handler may use the :proceed-types message to get a list of the available
proceed types, or it may use the :proceed-type-p message to check a particu-
lar candidate. Having chosen a proceed type, the handler sends the condition
object a : proceed message with one or more arguments. The first argument is
the proceed type, and the rest are the arguments for that proceed type. Sending
the : proceed message should be the last thing the handler does. It should then
return immediately, propagating the values from the : proceed method back to
its caller. Determining the meaning of the returned values is the business of the
signaller only; the handler should not look at or do anything with these values.
The signal-proceed-case macro provides a convenient way to signal a proceedable
condition, choose which of the defined proceed types for that flavor of condition
should be considered available to the handlers, and specify separate actions to take
Section 9.5 More on Proceeding 181
for each of the proceed types (after the : proceed method returns).
Chapter 10
THE MOVING ICONS EXAMPLE
The original version of the moving icons example was put together by Ken Church
for a class he taught in 1984. I've made some cosmetic modifications, and updated
it to take advantage of some more recently written support software. The basic
idea is that you have a frame split into two panes: a command menu of icons the
user may "pick up" with the mouse, and an initially empty pane where the user
may drop icons that have been picked up.
As before, if your site has loaded the tape that comes with the book, do Load
System moving-icons [or (make-system ' moving- i cons )] to load the
code. But this time, to make a frame and start the action, get the system menu
and click on Moving Icons at the lower right. Click left over one of the icons to
pick it up, move the mouse over the main pane, and drop the icon by clicking left
again.
10.1 General
It's my impression that there are four concepts in this short piece of code which are
new: using Zmacs-style command tables ("comtabs"), pop-up mini-buffers,
184 THE MOVING ICONS EXAMPLE Chapter 10
typeout windows, and changing the appearance of the mouse blinker. Most of the
dirty work involved in getting the first three to work is taken care of by the "corn-
tab" system (locally written software which both Ken and I have worked on). The
zwei:window-with-comtab flavor captures most of the fruits of this work, and is
itself quite easy to use. Another portion of the support code redefines many inter-
nal editor functions for reading typein from the mini-buffer to also work outside the
editor, with pop-up mini-buffers.
A comtab associates characters with functions, so that whenever a certain key is
pressed the corresponding function is called. In the editor, for instance. #\c-F is
bound to the function corn-forward, which moves the cursor forward. ("Charac-
ters" includes mouse characters, so functions may also be bound to particular
mouse clicks.) In addition to single key commands, a comtab may include
extended commands. These are accessible via Meta-X. You then type in the full
name of a command to a mini-buffer. (Completion is active, so you frequently
need only to type a few characters.) If you're in the editor proper, the permanent
mini-buffer at the bottom of the screen is used. If you're using the window-with-
comtab, a temporary mini-buffer pops up.
The window-with-comtab flavor has a comtab instance variable, and also includes
the flavor tv:process-mixin. The top-level function for the process is a loop which
reads characters and looks them up in the window's own comtab, calling the func-
tions that are found. (It also does something appropriate if it sees a blip instead of
a character.) All you need do is define the functions and put them in the comtab.
By copying so closely the mechanism used by the editor, we are able to use many of
the editor functions without change (or with small changes), most notably the on-
line documentation features. The Help key is active in any window built on
window-with-comtab, providing various functions for finding out about the currently
defined commands. And a number of extended commands have also been brought
over from the editor, including Edit Key, Edit Extended Command, Lookup Key
Bindings, Describe Key Bindings, Set Variable, and List Variables.
Any window built on window-with-comtab will also have a typeout window associ-
ated with it. Just as in the editor, if you hit the Suspend key, the typeout window
will become exposed, and you will enter a break loop, so that you may type arbi-
trary lisp forms to be evaluated.
Let's take a look at the code now.
Section 10. 1 General 185
10.2 moving-icons-frame
We have two panes, the command menu and the main pane. The frame is a bor-
dered constraint frame with shared io-buffer, so that the process running in the
main pane will see the blips from the command menu. The only new stuff is in the
default-init-plist specified for the menu. The most interesting is the specified value
for : item-list. As in the tree example of three weeks ago, we've used the
"general" item type. Each element of the list, corresponding to one item in the
menu, is a list of five elements. The first is the string which will be printed to
display the item in the menu. As you can see from the form of the loop, the string
will be one character long for every item, with the character code ranging from 0 to
127. Since each item is of type :eval, the effect of executing an item will be to
evaluate the form following the :eval keyword. And the form sends the main
pane the : pick-up- icon message, with an argument of the item's character
code. (It helps to know that the window-with-comtab code takes care of binding
the variable zwei:*window-with-comtab* to the object which has the window-with-
comtab mixin, in this case the main pane.) So if I choose the item in the upper left
corner of the menu, the main pane will receive a : pick-up-icon message with
an argument of 0.
The :rows init spec instructs the menu to display its 128 items in 4 rows of 32
each, implying that it must do some horizontal spreading. And the : default-
font spec is crucial. Since the items are really just one-character strings, the way
they appear depends on what font the menu uses for printing them. By default,
command menus use a standard variable-width alphabetic font called jess 14. But
we've specified the font named mouse, which happens to contain many of the char-
acters which are used for the mouse blinker in various situations. You could use
any other font — either an existing one or one of your own creation — by changing
the : default-font spec (or by sending the : set-icon-font message, which
is done by Meta-X Set Icon Font). You can read about using fonts in section 12.7
of volume 7. See also the Zmacs commands Meta-X List Fonts and Meta-X
Display Font. For modifying a font or creating a new one, use the Font Editor,
which is described in Part II of volume 3.
10.3 moving-icons-main-pane
This flavor is built on zwekwindow-with-comtab, with tv:pane-no-mouse-select-mixin
added, to make it a pane, and one which will not confuse the select menu. An
instance variable keeps track of the current icon. The default-init-plist specifies
what comtab to use (*icon-comtab* is defined at the end of the file) and what font
the window should print with. This font obviously needs to match the font of the
186 THE MOVING ICONS EXAMPLE Chapter 10
command menu.
The first two methods are quite simple. The : set-icon-font method sets the
default font of the command menu (which causes it to redisplay all the items in the
new font) and sets the window's own font-map to match (which affects only subse-
quent typeout — the current display is left alone). The : pick-up- icon mes-
sage, recall, is sent when a menu item is executed. The method sets the icon-
character instance variable, and sends the window the : mouse-standard-
blinker message, which we'll see has the effect of making the mouse blinker look
like the item which was just chosen.
10.4 Messing with the mouse blinker
The easiest way to alter the appearance of the mouse blinker (though not the way
we do it in this example) is with the function tv:mouse-set-blinker. It has one
required argument — a keyword symbol — and two optional ones. The keyword
tells what sort of blinker to switch to, and the system must already have been told
how to get a blinker of that type. Teaching the system about new types of blinkers
is not difficult, but it's also not necessary in this case, so we don't need to go into it.
The reason it's not necessary is that there's already a kind of blinker which is
sufficiently flexible for our needs, and that is the : character blinker.
This sort of blinker has two instance variables, one specifying a font to use and one
a character code. The blinker draws itself as the given character in the given font.
So by adjusting the values of the instance variables, a single blinker can be made to
look like any character in any defined font. This is exactly the reason for the
existence of the "mouse" font; most of the various mouse blinkers you're used to
seeing are actually the same : character blinker, set to varying characters in the
mouse font.
And finally we come to the function tv:mouse-set-blinker-definition, which is what
the example uses in place of tv:mouse-set-blinker. The former takes several addi-
tional arguments, which allow you to send an arbitrary message to the "new"
blinker (which may really be the same object as the old blinker). The message we
send is : set-character, which takes two arguments, the new character code,
and (optionally) the font to use. In our case the character is the value of the
instance variable icon-character, which will have been set by the : pick-up- icon
method, and the font is the main pane's current font (accessed via the instance
variable tv: current-font), which we have been careful to make sure is always
the same as the menu's font. For a slightly different effect, type this at a lisp
listener:
Section 10.4 Messing with the mouse blinker 187
(tv: mouse-set-blinker-definition : character 0 0 t : set-character
#\M ' fonts :43vxms)
The second and third arguments to tv:mouse-set -blinker-definition specify the jc-
offset and y -offset for the blinker. By default, the offsets are 0, meaning that the
blinker draws itself with its upper left corner at the actual mouse position. Often
you would rather the blinker position itself differently, perhaps with its center over
the real mouse position. That's when you need to set non-zero offsets. In the
example, we use whatever offsets the previous blinker had, to minimize any
apparent motion of the mouse as we switch blinkers. And if you glance down at
the : drop-icon method you'll see we need to adjust for the offsets again when
drawing the icon on the main pane, so that the icon is placed exactly under where
the mouse appears to be.
But let's return to the : mouse- standard-blinker method. Why did I make
the : pick-up- icon method call this one to change the mouse blinker, instead of
just including the multiple-value-bind form directly in : pick-up-icon? And
why does : mouse-standard-blinker test whether icon-character has a
non-nil value when : pick-up-icon will have just given it one? The answer to
both questions is that the mouse process will occasionally be sending our window
the : mouse-standard-blinker message, and I want to do something
appropriate then as well as when : pick-up-icon sends the message. Exactly
when the mouse process sends : mouse-standard-blinker is difficult to
explain, but a reasonable approximation is that it will happen whenever the mouse
crosses into the window. There is a default handler for : mouse- standard-
blinker, which our window would have inherited, and its action is simply to pass
the same message up to the window's superior. (If all of the window's superiors
also follow the default route of passing the message up, it will eventually reach
tv: main- screen, which will restore the blinker to the familiar NNW arrow seen
in a lisp listener.) But our method supersedes the default one. If there is no
current icon-character, ours behaves just like the default; if there is an icon-
character, ours makes the mouse blinker look like the icon. Had I put the
tv:mouse-set-blinker-definition in : pick-up-icon and kept the default method
for : mouse-standard-blinker, we would have lost the special appearance of
the mouse blinker every time it crossed into the main pane, including the manda-
tory first trip down from the command menu.
10.5 The :drop-icon method
Dropping the icon involves setting the cursor position to the correct spot, outputting
the character, resetting the icon-character instance variable, and restoring the
188 THE MOVING ICONS EXAMPLE Chapter 10
mouse blinker to its usual form. The only tricky step is setting the cursor position
— the mouse position will be provided in terms of outside coordinates. These must
be converted to inside coordinates by subtracting the margin widths, and then
corrected for the blinker offsets.
10.6 Setting up the comtab
The way to define a command is with the defcom macro (in the zwei package).
The first argument is the name of the command, which should begin with "com-".
Then there's a documentation string, which the help key will know how to get at.
The third argument is a list of options which are not relevant outside the editor.
The rest is the body of the command. Note that there is no "argument list"; func-
tions defined with defcom are intended to look for their arguments as the value of
zwei:*numeric-arg*. Recall that "arguments" to editor functions are numbers,
struck with the control or meta keys down before the main command character is
pressed. The effect of the numeric control keys is to bind *numeric-arg* appropri-
ately.
Executing a defcom just defines a function and adds the name of the command and
its documentation to a list of all commands, but does not make the command
usable — it is not included in any comtab. What puts the command into a comtab
is the zwei:set -comtab function, set-comtab adds a list of character-command pairs
(its second argument) to an existing comtab (the first arg) . The third argument, if
present, is a list of extended commands, i.e., commands that will be accessible via
Meta-X. The list should be created with zwei:make-command-alist and may be
appended with the extended command list of some other comtab. A single com-
mand may appear in a comtab any number of times, paired with different charac-
ters and/or as an extended command. And it may simultaneously appear in any
number of different comtabs.
The function zweirset-comtab-indirection may be used to cause inheritance from
another comtab. Any characters not found in the current comtab will be looked up
in the second one. In particular, we need to inherit from *basic-comtab* (the one
defined with window-with-comtab) the numeric argument commands and the help
functions.
10.7 Getting in the System Menu
The last form in the file is what puts "Moving Icons" in the rightmost column of
the system menu. The function tv:add-to-system-menu-programs-column takes
Section 10.7 Getting in the System Menu 189
three arguments. The first, a string, is what you want to appear in the menu. The
second is a form to be evaluated should your item be chosen from the menu. This
is usually a call to the function tv:select-or-create-window-of-flavor, with the one
argument being the flavor of window you want created. Finally there's a documen-
tation string, which appears in the mouse documentation line whenever the mouse is
over your item in the system menu.
10.8 The Program
190
THE MOVING ICONS EXAMPLE
Chapter 10
Section 10.8 The Program 191
d 5?« fl-9 * -a
o) -h = e C > H «,
2 N " 8 B » " S3
C --* >— fl 1 •• — Q) 0>
m u en* <u 3 "O
•§ iS -H rH 3 O C <1> O U
O P gHH S ° ° ° i 6 CO
1 I 2 L£3 ti3s 3 is: 852*3 e
& i § * - 5 a S >~ g 2 2 8 * > *
2 •? Hss as » 13?l§?
£
O 0) o ^- -^ .0 3
+J 5 «S -P ««
0) C i-l +J 0)
B . . iw <D T>
<W > 4-1 01 ••
0) +J (U •• ~
192
THE MOVING ICONS EXAMPLE
Chapter 10
0
Pi
4-1
1
s
P
Pi
C
o
CU
•H
p
P
p
■iH
0
CO — >
u
R7
^
>
--»
>^
CO
p
CO ^
<D 1
p
p "- —
CO CU
^^
0)
p
0) 0) —
S CO
H
•— »
JJ
CO
cu
CO N 0)
0 Pi
■H
c
4-1
p
4-1 -H N
E O
C
En
o
4-1
u
4-1 CO -H
E
z
4H
1
0
P
03
U
O 1 CO
cu *
Xi ••
3
r O —
CO Cu —
>
03
--»
•H Pi
P -H
c
Eh » —
v
iH
P
4-1
X!
>>
P Ol-rl
cu
<D
2 -*
c
■rl
0)
4-1
0
U
cu
l> P Cn
M 03 P
P s
id n
E
O — -»
fa X ~
p p
c
C
(0
Pi E <g
C
s --»
c c
•H
>> o
0
■H 1 E
- *
0
J X
0 o
p
rH
O
0
rH P 1
* X
o
Z ^
4H <w
1 1
a)
XI
4-1
4-1
■H
6
xi 4h a
1 0) 0
Pi 1
id cu
•H
H > i-i
w <d
5 P
c
a
cu
0
P
X
(1) rH P
(0
CU
w 2 >
0) r-l
o
■H
CO
0)
I
10 •• ••
4H Pi
X!
J >H cu
C 3
u
rH
0
X
P
0)
PI > >
•h g
P
OWE
S ~ ^
— o3
•H
X)
0
U
(0
0 P P
E
4H
1
E
u
03
0
E
•> *
4H
p<
S co
— > Q)
■d
cu
P
0
•• 4H 4H
Pi ••
0
w
tM P4 —
p tj
,-»
P
>
p
03
»-»
E
> 4H 4H
0 -H
fu
CO w
c I
c
03
P
u
XJ
POO
u cu
P
P
fn
1 (U —
O P
o
■0
03
O
p
1 1
•H S
Pi
PI
&
a >i x
4-1 0)
o
C
T3
u
1
ID
»-»
-O X >>
N
0
0
PQ
W Eh
1 10
■H
id
C
03
P
M
Pi
Pi
P
4H
4-1
w — <
C ••
_
■P
(1)
Xi
CU
3
0
cu x >,
Pi Pi
1
H
o
a
CO
CO
u
to
•H
o
CO 1 1
0) 0
CU
PI
Z
o *-+
3
i
<-i
•H
_- CU 0)
P O
XI
0
H
Eh X 2
•H 0
+J
i
(V
XI
1
CO CO
--»
P -H
P
o
S
2 Cu
1 C
c
x
CO
«-~
a
a
— pj pt
p
* JL
•rl
1
H CU 1
4j a)
0
u
0
4-1
o
T3
0
4H 0 0
0)
o a
CO
1
S
1 Q Eh
<D e
4-1
■H
0
4-1
•rl
P
p
4H E E
M
0
cu
P
o
J 2 W
(0 »
1
a
E
0
P
03
•o
o
Pi
CU P
tr 0)
«
< t> o
?
p
•H
TJ
P
•H
X! TJ
Pi
CO
h
yo-
V
0)
0)
>t c
C
>,— ~
CU
rH
p ••
03
i
o m
V c
c
CU
M
0)
•H
03
CU
p
XI
XI
9
►J '-' CO
C id
c
C
a
4-1
4-1
P
c
4-1 CO
0
1
CO *
u
*
2
(0 O, 4-1
(0
■H
id
4-1
CU
CO
03
a 4-i o
03
x>
a xi
XI
w
O X o
Q, 1
CO
a
rH
a
0 X)
1
a
CU
o a
P
»-«
p
o id
03
Pi
2 U
1 p
-rl
XI
i
1
1
CU
cu
1 u
03
rH
id
p p
p
P
HP —
C CD
tH
c
1
f:
X
p
CO
c
XI
X 0
Xi
■H
X)
Q E
g
E O
S^EH
•H D>
•H
T)
■H
cu
53
•H
— CO
U
Pi
Pi
= 0
o
0
z
<d ••
03
a
P
03
*
0
flj
u
<d
o
4-1
u
H
— U
S
a
6
o
03
6
XI
c
E
6
^~
X) pi
Pi
p
P
S I
1
1
En
= X w
1 u
03
O Tl
i
c
■H
i
p
Pi o
0
cu
CO
0 Xi
Pi X!
W
2 J
co o
a
CO
•H
c
CO
•H
r-i
CO
cu
•H 1
u
p
i
O P
0
p
J
P P< W J
C -H
i
c
03
Pi
X) X)
P
a
p
Xt P
•H
u
V
•H -rH
CJ
•H
a.
c o a o
O P
p
0
u
P
0
1
1
O
0
u
1 0)
03
to
r. S
•H
?
2
o fa s u
o a>
c
u
CU
CO
O
cu
P
■H
o
03
0) CO
0
p
p»
a i
1
1
O
fa
•h a
0
•H
P
•H
p
3
CU
P
■H
P
pi ••
>, 03
0
o ?
P
£ U
fa
i 3
4-1
o
a)
CD
rH
CO
CU
1
03
rH
P
45
E
P 0
CU
0
? 2
&> to
C7>
ifl
CO
&> P
03
1
a
Cn X!
0) 4H
o
XI XI
CO
X)
•H
cu O
Pi ••
P
C
u
0
Pi
U
>
cu
n
Pi
O
> rH
1
L c
1
c
CU
2 J
•H >
<D
•H
03
0
•H
03
1
CO
CO
•H
1
1 0)
4H
PI
4H
i "d
E
•H
5
> P
CO
> X!
E
>
P
0)
0
>
C
0) CO
r-{
0
r-t
o »
0
s
N
0
0
o
0
03
rH
0
>
0
o
rH
0)
o
<U
o *
u
*
E TJ
e
1
exi
a e
p
E
o
ax»
CO
•H
CO
— c
4-1
c
4-1
U
•H
•H
•rl Pi
I "H
E
•H
P
0)
r-l
0
rH
P
>
T3
P CU
X)
tr xi
0 0)
0
0)
XI
xi co
CD
xs
u
(1)
n
C
H
p
fl
•O
P
rH CO
C
p
Pi
O S
u
>
U
0 -—
CO
0
•H
CO
0
0
3
CU
0
0
Pi —
cu
cu
0)
4H N
4H
N
X!
Xi
X)
u
E
CO
X!
c
e
CO
CO
CO
CU
CU
P XI Xl
p
crx»
+j
•H
—
P
^--
— »
— -
"—
XI XI
XI
XI
<D C
c
CU
-p
a
CD
CU
•• Pi
PI
E 0)
cu
e
CU
0)
a
4-1
E
4-1
•rl CU
■H
CU
<4-l CO
CO
4-1
CO
CO
4-1
■H
4-1
■H
CU CO
CU
CO
CD —
0)
0)
tt)
> —
£
T3
T3
xi
•o
N
N
Section 10.8 The Program 193
XX
Ifl
e
■o
a
0
s
o ^
*-+
3
to *
a
C Xi
p
^ 0)
O fl
c
j) a
O P
0
a
h a
<4-l
fl <0
0
1
p _
Oi o
c
«w a
C I
0
I 0
•H O
o
01 P
> -H
■H
C m
0 01
1
0
£ IT)
p
U co
= A
tt)
■H C
*
01
I 0
XI ••
1
Oi o
fl -H
a
C -H
P tt)
0
•H
B ?
*-«
o
> Xi
0 N
c
O P
O
0
v
6 -d
1 *
0
» £
4) £1
•H
p
co nS
1
01
p tt)
P P
ft
■H
0 P
to e
0
rH
g
> 0
ft 0
1-1
id
g
HJ p
CO o
1 1
1
13
iH
rH O
<4-l -H
tt) G
e
A
0
1 ft
P O
0
HJ
o
«H
<c o
0
I
1
O (0
tt) -H
CO
1
p *
*
T—
O
a
> D>
o
Xi
|
o
<tj
O C
•• c
a
r-\
1
p
T3 -H
•H 0
p
1
0)
o>
C S
tt) -H
B
tt) ^
0
•H fl
> -P
O
CO
s
u
> P
N O
u
53
ft
1 X)
~ tt)
O
tt)
P
a
e
•H
3
P P
* -H
0
/
tt)
c
(0 0
A X)
o
%
s
0)
tt) <+-!
fl C
•H
N
a
p
P -H
*
i
o s
a i
a
I 0
O Xi
£1
tt>
z
P -0
O fl
fl
p
0)
0 c
i -p
P
0)
c
1 -H
C g
B
^
0
P s
0 0
O
01
0
o
o o
u
H
0) fl
•H 1
1
O
iH
* p
p
P
Cr
tt) p
0)
tt)
n
CO o
P W
0)
•o
■H
.. 0)
(d ••
•o
>
> -H
> -H
■H
10
0
P tt)
<4-l tt)
<D
s
^ CO
tt) >
s
>
r
X E
■O N
N
p
194 THE MOVING ICONS EXAMPLE Chapter 10
10.9 Problem Set #9
Questions
1. Make it possible to individually erase icons that have been drawn on the
main pane. (I think the best way to do so would be to use the mouse sensi-
tive items facility. You might also try doing it on your own, without mouse
sensitive items. That would probably entail duplicating some of the ms-item
stuff, but the limited functionality required here shouldn't involve too much
duplication.)
2. If you've chosen the mouse sensitive item route for (1), add some more func-
tions to the menu. For example, one option might ask for a keystroke then
change the icon to the one corresponding to that character code (in the same
font) . Another might prompt for the name of a font then redraw the icon in
that font (with the same character code) .
3. In an earlier version of the window-with-comtab support code, the typeout
window didn't restore the image of the window underneath it upon being
deexposed. The main pane therefore needed to be able to regenerate the pic-
ture. Though it's no longer strictly necessary to have such a : redisplay
method, it's still a good exercise.* Your solution to (1) should give you some
way of knowing what icons are currently displayed and where. Use this
knowledge to write the : redisplay method — it should do a : clear-
window and then redraw the current icons. While you're at it, make the
: redisplay method accessible through the comtab, via #\c-L and
#\ref resh.
Sorry for resorting to such a pedantic rationale. I didn't plan it this way, but at the last minute I
ruined what had been a perfectly good problem through my own industrious improvements to the
comtab code.
Section 10.9 Problem Set #9 195
Hints
Don't forget to put your code in the "icons" package!
1. Adding mouse-sensitivity involves the following steps: mix tv:basic-mouse-
sensitive-items into moving-icons-main-pane; make an item type alist with
tv:add-typeout-item-type, and put it on main-pane's default plist; write the
function(s) to be called when an item is chosen; and alter the : drop-icon
method to use the : item or : primitive-item messages. (Processing
the blip and calling the indicated function will be taken care of by the
window-with-comtab mixin; your function will be called with one argument,
the item.)
To erase the icon, draw over it in xor mode. It's probably easiest to use the
.-draw- char message. Since the icons themselves are just character codes,
and don't contain information about their screen position, you'll probably
want your mouse-sensitive items to actually be some sort of data structure
containing the x and y coordinates as well as the character itself.
2. Erase the icon as in (1), then redraw it with the new character or font.
: draw- char is probably the easiest way again.
3. Assuming again that you've chosen the mouse-sensitive-items route, you'll
need to get at the list of current items in order to redraw each of them. The
list is the value of the instance variable tv.item-list. (Remember to grab the
list before doing the : clear-window, because the : clear-window will
set the list to nil.)
196 THE MOVING ICONS EXAMPLE Chapter 10
Solutions
1 . The structure representing the item is a list of the character code, the current
font, and the x and y coordinates. The advantage of including the font is
that if Meta-X Set Icon Font is done, we can still properly erase old icons in
the original font.
( def flavor moving-icons-main-pane
( ( icon-character nil ) )
(tv:pane-no-mouse-select-mixin
tv: basic-mouse-sensitive- items new
zwei : window-with-comtab )
: settable-instance-variables
( :default-init-plist :comtab *icon-comtab*
: item-type-alist *icon-item-type-alist*
: font-map '( fonts : mouse) \new
:blinker-p nil
:save-bits t ) )
(defvar *icon-item-type-alist* nil)
(tvradd-typeout- item- type *icon-item-type-alist* : icon "Erase"
erase-icon t "Erase this icon.")
(def method { moving-icons-main-pane : drop-icon)
( mouse -x mouse-y)
(if (not icon-character) (beep)
(multiple-value-bind (x-off y-off)
(send tv: mouse-blinker : offsets)
(send self : draw-icon
(list icon-character tv: current-font
(- mouse-x x-off tv: left-margin-size)
(- mouse-y y-off tv: top-margin-size) )
:add))
(setq icon-character nil)
(send self : mouse-standard-blinker )) )
(defmethod (moving-icons-main-pane :draw-icon)
(char-info item-action)
(destructuring-bind (char font x y) char-info
(send self : draw-char font char x y tv:alu-xor)
(selectq item-action
Section 10.9 Problem Set #9 197
(:add (send self .'primitive-item
:icon char-info x y
(+ x (send self : character-width char font))
(+ y (font-char-height font)
(send self :vsp) ) ) )
(: delete (setq tv:item-list
(del #' (lambda (item list)
(eq item (second list)))
char-info tv: item-list ) ) ) ) ) )
(defun erase-icon (char-info)
(send zwei : *window-with-comtab*
: draw-icon char-info : delete))
( tv : add- typeout- item- type
♦icon-item-type-alist* : icon "Change Character"
change-icon-char nil
"Replace this icon with a different character.")
(defun change-icon-char (char-info)
(let ((new-char (read-single-char)))
(process-sleep 30)
When the typeout window is deexposed, it restores the window
underneath to how it was when the typeout window was first
exposed, so if we leave the typeout window exposed while we change
the display, our changes will be lost when it does get deexposed.
(send zwei : *typeout-window* rdeexpose)
( send zwei : *window-with-comtab*
: draw-icon char-info : delete)
(setf (first char-info) new-char)
(send zwei : *window-with-comtab*
:draw-icon char-info :add)))
(defun read-single-char nil this is borrowed from the code
zwei : ( LET ( CHAR ) for Help-C (com -self- document)
(TYPEIN-LINE "New char: ")
(SETQ CHAR (TYPEIN-LINE-ACTIVATE
(EDITOR-INPUT :MOUSE :RETURN) ) )
(TYPEIN-LINE-MORE "~:(5>C" CHAR)
char ) )
( tv : add- typeout- item- type
♦icon-item-type-alist* :icon "Change Font" change-icon-font
198 THE MOVING ICONS EXAMPLE Chapter 10
nil "Redraw this character in a different font.")
(defun change-icon-font (char-info)
(let ( (new- font (read-font-f rom-mini-buf f er ) ) )
(send zwei : *window-with-comtab*
: draw-icon char-info : delete)
(setf (second char-info) new-font)
(send zwei: *window-with-comtab*
: draw-icon char-info :add) ) )
where ( read-f ont-f rom-mini-buf fer) is the body of com -set -icon -
font starting at ( cdr . . . ) , and com-set-icon-font should be changed to
call read-font...
3. In addition to the redisplay, I've also thrown in a com-clear-window to
remove all the current icons.
(defmethod (moving-icons-main-pane :redisplay) nil
(let ((old-item-list tv: item-list ) )
(send self : clear-window)
(loop for (nil item) in old-item-list
do (send zwei : *window-with-comtab*
:draw-icon item :add))))
( zwei : def com com-redisplay
"Regenerates display from current dropped icons"
nil
( send zwei : *window-with-comtab* : redisplay) )
(zwei: def com com-clear-window
"Removes all dropped icons from the window" nil
(send zwei : *window-with-comtab* : clear-window) )
(zwei:set-comtab *icon-comtab*
'(#\c-L com-redisplay
#\refresh com-redisplay
#\clear-input com-clear-window) )
Chapter 11
MORE ADVANCED USE OF THE EDITOR
The standard Zmacs commands are generally quite well documented by the on-line
help facilities. So there should be no difficulty in becoming fluent in the use of the
built-in commands simply by consulting the automatic documentation. Or, if you
prefer, many of the more common built-in commands are described on paper, in
Part I of volume 4.
The methods for adding new commands, on the other hand, are not documented so
completely. It is upon that topic that this class will concentrate.
11.1 Keyboard Macros
keyboard macros allow you to bundle up any number of keystrokes and execute
them all with one keystroke. (These actually are documented, but since they fit in
with the rest of today's chapter, I thought we should look at them as well.) The
Zmacs command "c-X (" starts a keyboard macro. Whatever keys you press
from then up until you type a "c-X )" are remembered while they are executed.
When you type the c-X ) the macro will be defined. It can be re-executed by
typing c-X E. The effect will be as though you had typed all the keystrokes in
200 MORE ADVANCED USE OF THE EDITOR Chapter 11
the macro definition (but faster). Giving a numeric argument to c-X E will cause
the macro to be repeated that many times.
c-X E always executes the most recently defined macro, so if you define another
macro with c-X (, the definition of the first one will be lost, unless you have pre-
viously saved it somehow. One way of saving a macro is to give it a name, with
M-x Name Last Kbd Macro. Once a macro has been named, you can install it on
a key with M-x Install Macro. The name of the macro and the keystroke on which
to install it will be prompted for in the mini-buffer. From then on (until you dein-
stall the macro, or install some other command on the same key), typing that key-
stroke will execute the macro. When M-x Install Macro asks for the name of the
macro to install, if you just type a carriage return instead of the name of a macro,
the one most recently defined will be installed on the specified key. It's therefore
unnecessary to ever name your macros, as long as you install them before defining
another.
Here's a simple example, something which I often did while working on the early
versions of these lectures, in the troff text formatter. I'll define a keyboard macro
for inserting the troff directives for switching to an italic font and back, and install
it on super-I.
c-x ( start keyboard macro definition
\f i\f p insert the text
c-B c-B c-B move the cursor back to the correct position for inserting
italic text
c-x ) end macro definition
M-x install Macro [prompted with "Name of macro to install (CR for last
macro defined):"]
<return> [prompted with "Key to get it:"]
s-i [menu pops up for choosing which comtab to use]
<click on Zmacs>
The next step, one which I've been too lazy to ever take, but really should, would
be to put something in my login init file to automatically define this keyboard
macro every time I login. As things stand, I have to run through the above
sequence of commands whenever I start writing a text file (unless the machine
hasn't been booted since the last time I defined the macro). The way to define a
keyboard macro in lisp code is with the function zwekdefine-keyboard-macro. The
first argument is the name the macro will have, the second (usually nil) indicates a
repeat count, and the rest are the character codes for the keystrokes. Once the
macro has been defined, you can insert it into a comtab. So if I weren't so lazy, I
would add this to my init file:
Section 11.1 Keyboard Macros 201
(zwei: DEFINE -KEYBOARD -MACRO italic-font (nil)
#\\ #\f #\I #\\ #\f #\P #\c-B #\c-B #\c-B)
(zwei : command- store ( zwei : make-macro-command : italic-font )
#\s-I
zwei : *zmacs-comtab* )
If I were adding several macros at one time, and to the same comtab, I would use
set-comtab rather than command-store. And as for the choice of comtab, specify-
ing *zmacs-comtab* is equivalent to clicking on "Zmacs" in the menu that M-x
Install Macro pops up, and *standard-comtab* is equivalent to clicking on "Zwei."
Each editor has its own comtab, but they are all indirected to *zmacs-comtab*, so
putting a command there means that it will be accessible in all instances of the
Zmacs editor, unless of course the command is shadowed by a binding to the same
key in an individual editor's comtab. And *zmacs-comtab* is indirected to
*standard-comtab*, as are all the other zwei-based editors* (e.g., Zmail and Con-
verse). So a command inserted in *standard-comtab* will be available in all the
zwei editors, unless shadowed.
11.2 Writing New Commands
Most extensions to the editor are not expressible as a sequence of keystrokes. For
these you need to write a function, with defcom, and then add it to the comtab of
your choice with command-store or set-comtab. Among the things you may want to
do from your function are: insert text into a buffer, read text out of a buffer, get
user input from the mini-buffer, and send text to the typeout window. All of these
are reasonably straightforward, once you know about a few key variables and func-
tions.
11.3 Buffers and Streams
The functions zwekopen-editor-stream and zwei:with-editor-stream open a bidirec-
tional stream to an editor buffer. They are analogous to open and with-open-file in
that open-editor-stream simply creates the stream and returns it, while with-editor-
stream puts a call to open-editor-stream inside a useful wrapper, and so is prefer-
able if your control structure allows it. (The wrapper in this case guarantees not a
As I understand it, eine and zwei, apart from being "one" and "two" in German, were the names of
the first two text editors written for lisp machines. They are acronyms, respectively, for Eine Is Not
Emacs, and Zwei Was Eine Initially.
202 MORE ADVANCED USE OF THE EDITOR Chapter 11
close, which isn't meaningful for editor streams, but a : force-redisplay, so
any changes to the buffer will be apparent.) Either of these functions ultimately
ends up calling interval-stream, which does a make-instance of flavor interval-
stream. For more control over how the stream is made, you may wish to call
interval-stream directly. But in general, open-editor-stream provides a good
higher-level interface.
There is some documentation on these two functions in chapter 44 of volume 7,
mainly on the various options for specifying which buffer the stream should point
to, and where in the buffer it should initially point. You must specify at least one
of the following options: : interval, : buffer-name, : pathname, : win-
dow, or : start. : buffer-name and : pathname are easy enough. If a
buffer exists which matches the given information, it is used; if not, one is created
(unless the :create-p option has been used to specify otherwise). Understand-
ing the others requires knowing a bit more about the editor data structures.
The base flavor for all buffers is zwekinterval. The value of the variable
zwei:*interval* will be the current buffer, an object whose flavor is likely to be
something like zwehfile-buffer, which is indirectly built on interval (via node, top-
level-node and buffer). Thus references to the current "interval" mean the current
buffer. An interval may also be created to contain any portion of a buffer; some
intervals are actually buffers, while many others are temporary objects used to
point to arbitrary regions of text. An object of flavor interval-stream (see above) is
simply a stream whose "peripheral device" is an interval.
The interval flavor has (among others) two instance variables, for the beginning and
end of the text it refers to. These two are represented as buffer pointers, a type of
object defined by the zweirbp defstruct. A bp is a list consisting of three elements:
an object of type line (defined by the zwekline defstruct), an index into the line,
and a keyword we needn't be concerned with here. A line, in turn, is a string (the
text of the line) with all kinds of information in the string's array-leader, most
importantly pointers to the next and previous lines in the buffer. So given an inter-
val you can get bp's for the first and last character positions in the buffer, given a
bp you can tell what line it refers to (as well as which character in the line), and
given a line you can find the preceding and following lines.
The value of the variable zwei:*window* is an object of the type defined by the
defstruct zwei:window (not to be confused with objects of type tvrwindow). It con-
tains information about the portion of the buffer currently visible. Among its slots
are a pointer to the interval (buffer) that window is displaying part of, a bp for the
position of point (the cursor), a bp for the first character in the line currently
displayed at the top of the screen, and a count of how many lines are visible. The
Section 11.3 Buffers and Streams 203
window defstruct uses the : array- leader type, meaning that the object is an
array, with all the slots going into the array-leader. The array itself contains a row
of information for each line in the area the window maps to, including the "line"
object.
The macro zwei:point, called with no arguments, returns the bp for the current
point. As you might expect, the macro expands into ( ZWEI : WINDOW-POINT
ZWEI : *WINDOW*), the accessor for the "point" slot of the current window. A
similar macro, zwei:mark, returns a bp for the most recently dropped mark, which
is another slot in the window defstruct.
Now back to the options for open-editor-stream. The ones we delayed discussing
were : interval, : window and : start. We're now in a position to make
sense out of these. If you use the : interval option, the value supplied should be
an interval; you may use zwei:* interval* or any of a number of functions which
exist to create an interval pointing to an arbitrary text area. The stream created
will read from and write to the given interval. The : window option is similar; you
provide a (zwei) window, and open-editor-stream returns a stream into that win-
dow. The : start option is a little more complicated. It may be used either
alone or in combination with other options. If the value you provide is a bp, the
stream will begin at the specified bp. In this case no other options need be sup-
plied, and the stream's "end of file" will be at the end of the buffer containing the
bp. (You can force some other termination point with the : end option.) The
other possible values for : start are all keywords, and all require that some other
option (like : interval or : window) indicate which buffer to use. The effect of
the : start keyword will be to determine where within the buffer the stream will
start. Among your choices are : beginning, :end, :mark, : point, and
: region.
There are quite a few other options to open-editor-stream to control various details
of its behavior, but the ones we've already seen are sufficient to write all sorts of
useful applications. You'll find many examples in the editor code to use as models.
These are likely to use interval-stream directly, rather than through open-editor-
stream, because open-editor-stream is a relatively new feature. Your own functions
will probably be clearer and easier to write if you use open-editor-stream (or with-
edi tor-stream ) .
Here are a few trivial examples, to illustrate the basic concepts. (All assume the
current package is zwei.)
(with-editor- stream (str : interval *interval*)
(send str : string-out
204 MORE ADVANCED USE OF THE EDITOR Chapter 11
"surprise text inserted at end of current buffer"))
(with-edi tor- stream ( str : interval *interval* : start : beginning)
( send str : string-out
"surprise text inserted at beginning of current buffer"))
(with-editor-stream (str : start (point))
(send str : string-out "surprise text inserted at point"))
(with-editor-stream (str :buffer-name "mbox //usr//hjb// S:"
: start : beginning)
(send str : line-in)) returns first line of buffer containing my mbox file
One slightly subtle point to keep in mind is that the variables *interval* and •win-
dow* are not global; they're bound partway down the stack in the Zmacs process.
This makes no difference if you're writing editor commands, because they'll be exe-
cuted in the same process. But it does mean you'll have to be careful if you write
code intended to interact with the editor's buffers from another process, because
those variables will then be unbound. In particular, of the four examples just given,
the first three would bomb if evaluated in a lisp listener (the third because point
has to access *window*), while the fourth would work. All four work fine from the
editor's typeout window (as long as the package is correct), since the typeout
window's break loop is in the Zmacs process.
11.4 Reading from the Mini-buffer
Another set of tools often used in writing editor commands are the functions for
reading from the mini-buffer. There are many — their names generally begin with
"typein-line-," and the varieties differ in how they know when the typein is com-
pleted, and in what form they return the typein. Here are a few of them (all in the
zwei package):
typein-line-readline ctl-string &rest args
A prompt (created by format using ctl-string and args) is printed in the
typein line (just above the mini-buffer — you've seen it used frequently),
and keyboard input is directed to the mini-buffer. When the Return or End
keys are pressed, a string is constructed out of all the preceding characters
and returned. The behavior is analogous to that of the readline function.
typein-line-read ctl-string &rest args
Section 11.4 Reading from the Mini-buffer 205
Same as above except the lisp reader scans the string before it is returned,
and the lisp object returned by the reader is returned from typein-line-read.
The behavior is roughly analogous to that of the read function.
typein-line-form-to-eval prompt &optional initial -contents initial -char -pos
Similar to typein-line-readline except that the Return key does not terminate
input — it simply moves to a new line. Only the End key terminates input.
A string is returned (possibly containing Return characters).
typein-line-multi-line-read ctl-string &rest args
Combination of typein-line-read and typein-line-form-to-eval. Multiple lines
are read, terminated by End, and the input is scanned by the lisp reader
before being returned.
read -buffer-name prompt default &optional impossible-is-ok-p
Prints prompt in the typein line, and does a completing read in the mini-
buffer, using the names of all the buffers as the set of possible completions.
default is chosen if the user just types Return. The actual buffer object
corresponding to the selected string is returned.
typein-line-completing-read history default blank-line-defaults prompt alist
&optional ...
The function called by read-buffer-name (and many others) to do the com-
pleting read. It has more options than you even want to think about (until
one of them turns out to be exactly the thing you need), all of which are
described in great detail at the function definition. It allows for the use of a
history as well as a default, so you can pop your way through the history to
reuse earlier inputs.
com pie ting-read -from -mini -buffer prompt * completing- alist* &optional ...
The function called by typein-line-completing-read (and many others) to
really do the completing read. You may want to call it directly because it
has a somewhat simpler interface, if fewer facilities.
read-defaulted-pathname prompt * reading-pathname -defaults* &optional ...
Used by c-X c-F (and many others) to read a pathname and merge it
206 MORE ADVANCED USE OF THE EDITOR Chapter 11
with some set of defaults.
11.5 A Real Example
Here's something taken out of the editor code, the definition for M-x Evaluate Into
Buffer.
(DEFCOM COM-EVALUATE-INTO-BUFFER
"Evaluates forms from the minibuffer and inserts the
results into the buffer. You enter Lisp forms in the minibuffer,
which are evaluated when you press END. The result of each
evaluation appears in the buffer before point. With a numeric
argument, it also inserts any typeout that occurs during the
evaluation into the buffer." (KM)
(LET ((FORM-STRING (TYPEIN-LINE-FORM-TO-EVAL
"Lisp forms to evaluate:"))
(OUTPUT- STREAM ( INTERVAL-STREAM- INTO-BP (POINT)))
FORM)
(WITH-INPUT-FROM-STRING (INPUT-STREAM FORM-STRING)
(LOOP DO (CONDITION-CASE (ERROR)
(SETQ FORM (READ INPUT-STREAM) )
(SYS:END-OF-FILE (RETURN DIS-TEXT))
(SYS: READ-ERROR (BARF ""A" ERROR)))
(FORMAT OUTPUT-STREAM "-{-&-S-}"
(LET-IF *NUMERIC-ARG-P*
((STANDARD-OUTPUT OUTPUT- STREAM) )
(MULTIPLE-VALUE-LIST ( EVAL FORM))))
(MOVE-BP (POINT)
(FUNCALL OUTPUT-STREAM ' :READ-BP) ) ) ) ) )
typein-line-form-to-eval returns a string, presumably containing lisp forms.
( interval-stream-into-bp (point)) is like (open-editor-stream
: start (point) :end (point)). We make input- stream point to the
string of forms, and then loop, reading one form at a time from the string. For
each one, assuming there are no errors, we use format to print the results of
evaluating the form into the buffer, via the open editor stream, and move point.
Section 11.5 A Real Example 207
.6 Problem Set #10
Questions
1. Write com-comment-out-lines-in-region, and com-uncomment-lines-in-region,
to insert (and remove) semi-colons at the beginning of each line in the region.
(Both of these already exist as parts of com-comment-out-region, but that
version includes lots of hair for handling messy cases. Write something sim-
ple.)
2. Write com-insert-text-into-other-buffer, which should read any amount of
text from the mini-buffer (terminated by #\End), prompt for the name of a
buffer, and append the text to the end of the given buffer.
3. Write a macro to be used either inside or outside the editor, which redirects
all typeout during execution of its body to a newly created editor buffer.
208 MORE ADVANCED USE OF THE EDITOR Chapter 11
Solutions
(This all goes in the zwei package.)
1. (defcom com-comment-out-lines-in-region
"Comments out each line in the region." nil
(region-lines (start end)
(loop for line = start then (line-next line)
until (eq line end)
do (insert (create-bp line 0) #\;)))
dis-text)
( defcom com-uncomment-lines-in-region
"Removes semi-colons from beginning of each line in
region." nil
(region-lines (start end)
(loop for line = start then (line-next line)
until (eq line end)
when (char-equal (aref line 0) #\;)
do (let ((end-idx ( string-search-not-char
#\; line 1))
(start-bp (create-bp line 0)))
(delete-interval
start-bp
(if (null end-idx) (end-line start-bp)
(forward-char start-bp end-idx))))))
dis-text)
Don't forget to add the commands to a comtab so you can use them. Do
something like:
(set-comtab *zmacs-comtab*
' (#\s-; com-comment-out-lines-in-region
#\h-; com-uncomment-lines-in-region)
( make-command-alist
' ( com-comment-out-lines-in-region
com-uncomment-lines-in-region) ) )
2. (defcom com-insert-text-into-other-buf fer
"Appends text from the mini-buffer to the end of
any buffer." nil
(let ((text (typein-line-form-to-eval
"Text to append to other buffer:"))
Section 11.6 Problem Set #10 209
(buffer (read-buffer-name "Buffer to append text to:"
: other ) ) )
(with-editor-stream ( str : interval buffer)
(send str : string-out text)))
dis-none)
3. Anything sent to standard-output during execution of body will be inserted
into a buffer named buffer-name. There will also be messages inserted
before and after body is executed.
(def macro with-output-to-editor-buf fer ((buffer-name)
Sibody body)
* (with-editor-stream (standard-output
: buffer-name , buffer-name)
(format t "-2X;;; Diverting to buffer ( ~\datime\) ~2%" )
, @>body
(format t "-2%;;; End of diversion ( -\datime\) " ) ) )
Sample usage:
(with-output-to-editor-buf fer ( "test" )
(princ "The contents of the FEP file system follows:")
(print-disk-label) )
Chapter 12
A QUICK LOOK AT "THE NETWORK"
Although it is common to refer to a lisp machine's connections to the rest of the
world as "the network," as if the machine were connected via a single mechanism
to a unified system of linkages, such is not the case. There are several means of
communication, operating via several different hardware and software protocols.
And there is considerable overlap, with different software protocols operating simul-
taneously over the same hardware. It's not really very complicated, but it's easy to
become highly confused if the basic issues are not kept clear.
12.1 The "Gee-Whiz" Overview
The first distinction to keep in mind is between hardware and software. The
hardware basis for any given network service will be something like coaxial cable
and transceiver boxes, or a serial line. The software utilizing this hardware, say
"Internet" or "Chaosnet," must itself be viewed as composed of several theoreti-
cally independent levels. The various pieces of this modularity are coordinated by
the namespace database. The terms used by the namespace for the different levels
are service, protocol, and medium. (Don't be concerned if the exact meaning of the
various concepts is not clear at this point; all that matters for now is that you have
212 A QUICK LOOK A T "THE NETWORK" Chapter 1 2
a general understanding of what sorts of entities the terms refer to.) The distinc-
tions may at times appear somewhat strained or artificial, but in general a service is
the highest level entity, describing what the user wants out of the network connec-
tion. A service may be discussed without reference to which network is providing
it. Typical services are file transfer and remote login. Each network has its own
protocol (s) for providing any particular service. Chaosnet provides file transfer ser-
vice via the "qfile" protocol, while Internet provides the same service via the "tftp"
protocol. A medium is a mode of connection, like "byte-stream" or "chaos"; each
protocol requires some medium as the minimal type of connection which must be
present for the protocol to operate. A connection can be made to a remote host in
order to request a certain service when both the local and remote hosts are on a
network of a type adequate to support the medium required by the protocol under
which the remote host offers the desired service.
Let's look at excerpts from the namespace descriptions of two machines to see some
of this terminology in action.
HOST JONES
SYSTEM-TYPE UNIX
MACHINE-TYPE VAX
ADDRESS CHAOS 1015
ADDRESS INTERNET 192.11.39.9
ADDRESS TAMDHU- SERIAL 3
SERVICE FILE CHAOS QFILE
SERVICE LOGIN SERIAL- PSEUDONET TTY-LOGIN
SERVICE LOGIN TCP TELNET
This (partly fictional) fragment states that the UNIX host "jones" has addresses on
the chaos, internet and tamdhu-serial networks, and that it offers the file service
(file transfers) via the qfile protocol on the chaos medium, and the login service
(remote login) via either the tty-login protocol on the serial-pseudonet medium, or
the telnet protocol on the tcp medium.
HOST TAMDHU
SYSTEM-TYPE LISPM
MACHINE-TYPE LISPM
ADDRESS CHAOS 1003
ADDRESS TAMDHU-SERIAL 0
ADDRESS INTERNET 192.11.39.5
And this fragment tells us that the lisp machine "tamdhu" is on the chaos,
tamdhu-serial and internet networks. Combining this with the description of jones,
Section 12.1 The "Gee -Whiz" Overview 213
we can tell that from tamdhu a user could invoke file transfer service on jones,
since both machines are on the chaos network (and chaosnet supports the chaos
medium) . As for remote login service, the user gets a choice: s/he could invoke it
on the network called "tamdhu-serial" (which both hosts are on, and which sup-
ports the serial-pseudonet medium) or on Internet (which supports the tcp
medium) .
12.2 The Beginning of the Real Explanation
Until recently, what most people had in mind when they said "the network" with
reference to lisp machines was Chaosnet, a local area network developed in 1975 by
the MIT AI Lab, specifically for use as the medium for communications among lisp
machines. But as it was designed to minimize the difficulty of bringing other kinds
of machines into the network, by now quite a variety of computers — as well as
peripherals — may be connected via Chaosnet. The "chaos" in the name refers to
the lack of centralized control.
The hardware and software portions of Chaosnet, although designed for each other,
are logically independent. The Chaosnet software may operate on media other than
the Chaosnet hardware, and the software for other network protocols may use the
Chaosnet hardware. The transmission medium is sometimes called ethernet, which
can be misleading because the same term is applied to a type of network (including
hardware and software) developed by Xerox. But since the Ethernet hardware and
the Chaosnet hardware are largely compatible, the term "Ethernet" is frequently
applied to the hardware for any network using this sort of transmission medium,
regardless of which software protocol is in operation.
We'll return to Chaosnet shortly, but first let's complete the overview of the lisp
machine's network connections. Coexisting with Chaosnet on the ethernet cable is
the Internet, an entirely different software protocol. The hardware requirements of
the two types of network are compatible; although some kinds of computers use
different boards for the two interfaces, everything external to the individual
machines is identical. In the case of the lisp machine, even the internal hardware is
shared, so the distinction between the two networks is solely what happens to the
transmitted information in software. The software for Internet is not, however, part
of the basic lisp machine system. It's another product which must be purchased
separately, as "ip-tcp." The Bell Labs/Murray Hill lisp machine community has a
site license, and ip-tcp is installed on all our machines. As with Chaosnet, Internet
is understood by a wide variety of computers. In fact, Internet has become a
Department of Defense standard, so one may reasonably expect virtually all
manufacturers to support Internet in their new products, while Chaosnet may be
214 A QUICK LOOK AT "THE NETWORK" Chapter 12
expected to gradually fade from view.
The third means of communication available to the lisp machine is the serial i/o
facility. Each lisp machine has three serial ports, which may be used to communi-
cate with any device that understands the RS-232 protocol. At my site we connect
these ports to a VAX* terminal line, to allow use of the lisp machine as a terminal
logged in to a UNIX system, or to speech synthesizer boxes, or a modem. Because
the related software uses a construct called a serial-pseudonet, it appears from the
user's point of view that each lisp machine is the center of its own star-shaped net-
work (on an equal footing with Chaosnet and Internet) connecting it to the devices
at the other end of the serial lines.
So all together there are two kinds of physical connection to the outside world (eth-
ernet and serial line), and three kinds of conceptual connection (Chaosnet and
Internet via the single ethernet, and serial-pseudonet via any number of serial
lines). The term "network" — now that Chaosnet no longer stands alone — usu-
ally refers to these three conceptual connections, either one of them taken individu-
ally or the set of them considered collectively. It is possible, though unusual, to
define additional types of conceptual networks, sharing the existing physical connec-
tions.
Most of the physical and conceptual connections are documented in volume 9 of the
Symbolics manuals, from which I have borrowed for this chapter. Chaosnet, both
hardware ("ethernet") and low-level software, is covered in chapter 15. Part I of
volume 9 describes the namespace database and how the namespace manages
requests for network services. Serial i/o is in Part III of volume 5.
12.3 The Ethernet
The transmission medium (the ether) which supports both Chaosnet and Internet is
1/2 inch coaxial cable, with a transceiver box at each point where a machine joins
the cable. A single ether must be a linear cable, with no branches or loops. The
maximum length of an ether is about a kilometer. Multiple ethers may be joined
by bridges, i.e., machines on both of two ethers, which relay packets from one to
the other, so that the two subnets may act as one large ethernet without exceeding
the length limitation. For example, the portion of the Murray Hill ethernet that
local lisp machine users need to be concerned with has three single ethers joined by
bridges. One connects all the building 2 lisp machines (except Churchill), about
five VAXen and some Sun workstations; one (the "tempo net") connects a variety
VAX is a trademark of Digital Equipment Corporation.
Section 12.3 The Ethernet 215
of machines in building 3; the third (the "backbone") runs for about a mile (with
repeaters inserted at strategic points, since a mile is more than a kilometer) con-
necting the other two subnets and who knows what else, hitting about fifty
machines along the way. Sola (a VAX in building 2) serves as the bridge between
the building 2 subnet and the backbone; Vivace links the backbone and the tempo
net. The current list of subnets can be found in /etc/networks on any UNIX
machine running Internet.
Returning to the operation of a single ether, one machine at a time may seize the
ether and transmit a packet with the address of some other machine. The packet
will be seen by every machine connected to the ether; it is up to each to check the
address in the packet and decide (in hardware) whether it is the intended recipient.
If the address is correct, the packet is received and relayed up to the appropriate
software (Chaosnet or Internet, depending on the type of the packet). Otherwise it
is ignored.
The ether can tolerate temporary breaks in the cable for about a minute — long
enough to splice a transceiver in or out of the ether. The effects of longer breaks
vary with the type of the machine, but can be disastrous for some — VAXen crash
when reconnected after a long break. If such a machine is to be spliced out of the
network while running, a pair of terminators should be attached to its transceiver.
The terminators present an impedance similar to that of an intact ether, so the
machine sees what appears to be a legitimate ether which just happens to be com-
pletely inactive. It can later be reattached with no difficulty.
Lisp machines are insensitive to ether disruptions. To physically remove a lispm
from the network it is safe and easy to disconnect the transceiver cable where it
plugs into the back of the machine. You may reconnect at any time. Actually, it's
rarely necessary to physically disconnect the machine. The equivalent may be done
in software by evaluating (neti : reset ), which disables the ethernet interface,
(neti: enable) starts it up again. The CP command Reset Network does a
reset immediately followed by an enable.
12.4 Chaosnet
The previous section discussed the portion of Chaosnet which is shared with Inter-
net, i.e., the Ethernet hardware. Now a bit about the portions which are unique to
Chaosnet.
Chaosnet addresses are simple numbers, consisting of three or more octal digits.
The least significant eight bits indicate the machine's address on its own subnet, or
216 A QUICK LOOK AT "THE NETWORK" Chapter 12
single length of ethernet cable. The higher order bits indicate which subnet the
machine is on. The subnet codes used in Murray Hill are 1 for the backbone, 2 for
building 2, and 3 for the tempo net. So an octal address of 406 (Pancake's
address) means machine 06 on the backbone. Note that the numbering of
machines on a subnet has no relation to their physical order. A lisp machine's
opinion of what its own address is comes from the "Set Chaos-address" fep com-
mand. Every boot file should have one of these. The machine's opinion as to its
name is derived from its address: it believes whatever the namespace database says
is the mapping between names and addresses. So all that's required to make
Abelour (address 1002) believe that it's really Glengarioch (address 1006) is to
boot Abelour from a boot file that contains the line "Set Chaos-address 1006." No
physical alterations to the network connections are involved. Then Abelour will
receive and answer any packets intended for Glengarioch. There is no sense in
which Abelour will not, in fact, be Glengarioch. (To avoid having two Glen-
gariochs, this stunt should only be pulled when Glengarioch is down, or itself
booted as somebody else.) The file "fep0:> namespace. boot" on Abelour does
exactly this. We boot Abelour as Glengarioch if the real Glengarioch (our
namespace server) is down, so that Abelour will act as the namespace server until
Glengarioch is fixed.
The same method for address-swapping allows us to transform Laphroaig (1001)
into Ghost-of-Laphroaig (401), but since the new address implies being on a
different subnet, the switch additionally involves plugging the machine into a nor-
mally unused transceiver on the backbone. This trick allows us to transfer large
files (like world loads) to machines on the backbone without having to go through
the bridge. Executing a band transfer through a bridge takes at least twice as long
as going direct, as well as completely tying up the chaos server on the bridge for
the duration.
A UNIX machine's idea of who is at what address is based on the contents of its
host table. The Chaosnet host table is in the file
/usr/chaos/lib/libhosts/hosts. local. (The Internet host table is in /etc/hosts.)
From volume 9, 15.3: "The principal service provided by Chaosnet is a connection
between two user processes. This is a full-duplex reliable packet-transmission chan-
nel. The network undertakes never to garble, lose, duplicate, or resequence the
packets... When first establishing a connection, it is necessary for the two commun-
icating processes to contact each other... One process is designated the user, and the
other is designated the server. The server has some contact name [indicating the
type of service] to which it listens. The user process requests its local operating
system to connect it to the server, specifying the network address and contact name
of the server. The local operating system sends a message (a Request for
Section 12.4 Chaosnet 217
Connection, or RFC) to the remote operating system, which examines the contact
name and creates a connection to an existing listening process, or creates a new
server process and connects to it, or rejects the request."
The first option (an existing process) is used for simple requests that can be
answered quickly and easily, such as a request for the current time. The main
server process handles these itself. More elaborate requests, which require
extended attention and multiple interchange of packets, are handled by spawning a
new process for that purpose. On a UNIX chaos server, this means simply execut-
ing a file in the /usr/chaos/server directory. The name of the file will be exactly
the contact name that was used to request the connection. So, for example, if a lisp
machine wants to read a file from a UNIX machine, it would send an RFC packet
to the UNIX machine, with contact name "FILE." The chaos server on the UNIX
end, upon receiving the packet, would start up a process running the contents of the
file /usr/chaos/server/FILE. The chaos server would then route any further pack-
ets related to the file transfer to this new process. The effect is as though the FILE
process on the UNIX system and the process requesting the file transfer on the lisp
machine were communicating directly with each other.
From a lisp machine, the easiest way to establish a connection with a server process
on a remote host is with the function chaos:open-stream. The two required argu-
ments specify the host and the contact name. (Several optional keyword arguments
offer more detailed control of the type of connection.) If a connection is success-
fully established, chaos:open-stream returns an open stream object. You may then
use all the usual messages to read characters from and write characters to the
stream. Conversion to and from the packet level is completely transparent — all
the user sees is a character stream.
12.5 A Bit More on Serial Streams
The programmer interface to the serial i/o facility also works via lisp stream
objects. The function si:make-serial-stream returns an open stream to one of the
serial ports (which one may be specified with the :unit keyword argument). The
usual messages for reading and writing may again be used. Details are in chapters
21 and 22 of volume 5.
12.6 The Role of the Namespace
The namespace database coordinates interaction with the various network facilities.
It is always possible to directly manipulate the stream connections as outlined
218 A QUICK LOOK AT "THE NETWORK" Chapter 12
above, but this is necessary only if you are adding a new type of network service.
To invoke any existing service which has already been integrated into the
namespace, you may use the higher-level interface provided by the namespace.
This interface insulates the user from having to know any details about how the
connection is established, or even which network is used. The namespace takes care
of finding an appropriate path.
The simplest way to invoke a network service through the namespace is with the
function net:invoke-service-on-host. The first argument is the name of a defined ser-
vice, and the second is a host object. The local host will find the best path to the
target host, over all available network connections, and use it to invoke the named
service. So to obtain Sola's idea of what time it is, I could do (net: invoke-
service-on-host :time ( si : parse-host 's)). (Seeing as how Sola's
namespace entry says that it provides the time service via the time-simple
protocol on the chaos-simple medium, we can tell this will end up being a
Chaosnet transaction, but we needn't be concerned with that level of detail.)
In cases where you need some service but don't care on which host it is invoked, the
function net:hnd-paths-to-service (called with an argument of the service name)
returns a list of service access paths, one for each host which provides the service.
The list is sorted by decreasing desirability. Having decided which path to use, you
may invoke the service by calling net:invoke-service-access-path on the chosen ser-
vice access path.
Another option is to invoke a service simultaneously on more than one host, with
service futures. You can then pick the first or best of several responses without a
long waiting period. The handiest way to manipulate service futures is with the
macro net:invoke-multipIe-services. This example prints every host's idea of the
current time, ignoring those which fail to respond:
(defun all-hosts-time ()
( net : invoke-multiple-services
( (net :f ind-paths-to- service :time) (* 60 10) "Time")
(host time)
(sys : network-error nil) ; catch the error and do nothing
( : no-error
(format t "~&~A: ~ : [unknown- ; ~\TIME\- ] " host time time))))
Section 12.6 The Role of the Namespace 219
12.7 Troubleshooting
Typing Function-H [or evaluating (hostat)] will show which machines your
lispm is able to contact via Chaosnet. If you can't reach any remote hosts, some-
thing is probably wrong locally. If you can reach some and not others, something is
probably wrong out in the network somewhere. Here are a couple of hints:
Don't forget about netirreset and neti:enable. If a reset has been done, hostat will
fail everywhere.
The transceiver cable locks into place, at both the machine and transceiver ends,
with a tab which is slid sideways after the connector is pushed into the socket. A
fairly common cause of "network failure" is for the cable to fall off the back of the
machine because the tab wasn't used (or was defective).
Check the state of the process called "3600 Ethernet Receiver" in Peek. If it's not
"Ethernet Packet," something's wrong — maybe you ignored a Function-0-S
notification.
For more elaborate troubleshooting, try the "network" option in Peek.
APPENDIX: BASIC ZMACS COMMANDS
While it is true that there are a great many Zmacs commands, and trying to learn
them all would be a close to hopeless task, it is also true that the situation is really
much less difficult than it might at first appear. For one thing, you can edit files
quite effectively with a relatively small subset of the Zmacs commands. (This
should not be taken to imply that the remaining commands are superfluous; the
"quite effective" editing you can do without them is transformed into astonishingly
effective editing with them.) Another saving grace is that there is some pattern to
the pairing of keystrokes with editing functions. For instance, control characters
often act on single letters or lines; meta characters on words, sentences, or para-
graphs; and control-meta characters on lisp expressions. Thus c-F moves forward
one character, m-F moves forward one word, and c-m-F moves forward one lisp
expression. c-K means "kill" (delete) to the end of the line, m-K means kill to the
end of the sentence, and c-m-K means kill to the end of the current lisp expression.
So the amount of memorizing you have to do to start editing is really not very
great.
I can't overemphasize the utility of the Help facility in Zmacs. It can be a real
lifesaver, both when you don't know what commands there are to do something,
and when you've forgotten how to invoke a command you know about. So don't
limit yourself to the commands listed below. Consider the list a crutch, to help get
222
APPENDIX: BASIC ZMACS COMMANDS
you started, but try to leave it behind as soon as possible.
Movement Commands
C-F
c-B
C-N
c-P
C-A
C-E
mous
e left
Move forward one character
Move backward one character
Move down one line ("next")
Move up one line ("previous")
Move to the beginning of the line
Move to the end of the line
Move to mouse position
m-F
m-B
m-A
m-E
m-[
m-]
m-<
m->
Move forward one word
Move backward one word
Move to the beginning of the sentence
Move to the end of the sentence
Move to the beginning of the paragraph
Move to the end of the paragraph
Move to the beginning of the buffer
Move to the end of the buffer
c-m-F
c-m-B
c-m-A, c-
c-m-E, c-
m-[
m-]
Move forward one lisp expression
Move backward one lisp expression
Move to the beginning of the current definition
Move to the end of the current definition
Deletion Commands
c-D Delete forward one character
Rubout Delete backward one character
Clear Input Delete to the beginning of the line
c-K Delete to the end of the line
m-D Delete forward one word
m-Rubout Delete backward one word
m-K Delete forward one sentence
c-m-K
c-m-Rubout
Delete forward one lisp expression
Delete backward one lisp expression
APPENDIX: BASIC ZMACS COMMANDS
223
C-Y
m-Y
Restore ("yank") text deleted with any of the above, except c-D
and Rubout
Immediately following a c-Y or another m-Y, replace the yanked
text with the previous element of the kill history
c-Space
c-W
m-W
mouse middle
mouse drag left
Region Commands
Set the mark at the current position, and turn on the
"region." Subsequent movement commands will define the
region to be the area between the mark and the new
position.
Delete the region, putting it on the kill history
Put the region on the kill history without deleting it
Mark (make into the region) the object the mouse is
pointing at
Mark the area dragged over (between button press and
button release)
File Commands
C-X C-F
c-
-X
c-S
c
-X
c-W
M
-x
Compi
le
File
Read ("find") a file into its own buffer, creating an
empty buffer if the file doesn't exist
Write ("save") a buffer back to its file
Write a buffer to any file, specified by name
Compile a file, i.e., write a binary version of the file.
This has no effect on the current Lisp environment.
(Compare to M-x Compile Buffer.)
Buffer Commands
c-m-L Switch to the previous ("last") buffer
c-X B Switch to a buffer specified by name
c-X c-B Display a list of the buffers
c-sh-E
Lisp Commands
Evaluate (call the Lisp interpreter on) the region, if
224
APPENDIX: BASIC ZMACS COMMANDS
c-sh-C
M-x Evaluate Buffer
M-x Compile Buffer
End, s-E
it's active, or the current definition if it's not
Compile the region or current definition into the Lisp
environment
Evaluate the entire buffer
Compile the entire buffer into the environment. This
has no effect on the file system. (Compare to M-x
Compile File.)
Murray Hill Standard Utilities only. Evaluate the
region or current definition and insert the result into
the buffer.
Miscellaneous
Suspend Enter the typeout window. (Resume returns.)
m- . Find the definition of a given function
c-X D Directory edit — shows a directory listing and enables manipulation
of the files in it
Help A Apropos — list all commands containing a given substring
Help C Describe the command associated with a given keystroke
Help D Describe a command specified by name
INDEX
* variable 52-53
*catch special form 64
*throw function 64
: activate method 48
: alias-f or-selected-windows
method 87
:blinker-p init keyword 88
: bury method 49
: byte-size open option 126
: canonical-type method 133
:case method combination 180
: character blinker 186
: characters stream operation 121
: checker defresource option 163
: choose method 142-143
: clear- input stream operation 122
: clear-output stream operation
122
: clear-screen stream operation
123
: clear-window
method 76, 195
stream operation 123
: close stream operation 122
: compile make-system option 167
:compile-load defsystem clause
166
: component- systems defsystem
clause 167
: compute-all-handlers-once
method 40
rconfigurat ions init keyword 87
226
INDEX
: constructor defresource option
162
: creation-date stream operation
128
: deactivate method 48
rdeexpose method 48-49
: default-font init keyword 185
:default-init-plist defflavor
option 87-88
: describe method 31
: deselect method 48
rdevice method 133
: direction
open option 125
stream operation 121
: directory method 133
: do- components defsystem clause
167
: draw- char method 195
: draw-circle method 76
: draw- line method 36
: draw-lines method 36
: draw-point method 76
: draw-rectangle method 76
: draw- triangle method 76
: execute method 141-143
: expose method 48
: fas load defsystem clause 166
: finder defresource option 163
: finish stream operation 122
: force -output stream operation
122
: force-redisplay method 202
: free-list-size defresource
option 164
: get method 133
:get-handler-f or method 31
: gettable-instance-variables
defflavor option 19
; host method 133
; if -exists open option 126
; included-f lavors defflavor
option 23
: init method 84
; initable-instance-variables
defflavor option 20
; initial-copies defresource
option 163
; initializer defresource option
162
; inside-size method 76
litem method 90, 195
: item-list init keyword 185
ikill method 48
: length stream operation 128
: listen stream operation 121
: matcher defresource option 163
:menu blip 141-143
: method-combination defflavor
option 28
: module defsystem clause 166
: mouse -buttons method 142
: mouse-click,
essential-mouse's method for 30
method 29, 86, 89-90, 143
: mouse-moves method 90
: mouse-standard-blinker
method 186-187
:name method 133
: new-canonical -type method
133
: new-device method 133
: new-directory method 133
: new-name method 133
: new-pathname method 133
: new-raw-device method 133
: new-raw-directory method 133
: new-raw-name method 133
INDEX
227
: new-raw-type method 133
: new- type method 133
: new- vers ion method 133
: no- error condition-case clause 177
rnoconf irm make-system option
168
: or method combination 29, 89
: override method combination 89
: package defsystem clause 167
: panes init keyword 87
: patchable defsystem clause 167
: pathname stream operation 128
: pathname-default defsystem
clause 166
:plist method 133
: primitive-item method 90,195
: print -only make-system option
168
: print- self method 31,140
: proceed method 178,180
:proceed-type-p method 180
:proceed-types method 180
: process init keyword 88
: properties method 133
: putprop method 133
: raw-device method 133
: raw-directory method 133
: raw-name method 133
: raw- type method 133
:read-cursorpos stream operation
122
: read-pointer stream operation
123
: remprop method 133
: report method 175
: required-init-keywords
deffiavor option 84
:reverse-video-p method 76
:rows init keyword 185
select method 48, 87
selectable-windows method 87
selected-pane init keyword 87
send-if-handles method 84
set-character method 186
set-cursorpos stream operation
123
set-pointer stream operation 123
set-reverse-video-p method
76
settable- instance-variables
deffiavor option 19
: string-for-host method 133
: string-f or-printing method
133
: string- in stream operation 121
: string-out stream operation
120-121
: trans lated-pathname method
135
: truename stream operation 128
:tyi
method 76
stream operation 120, 126
: ty i -no-hang stream operation 1 22
: tyipeek stream operation 121
:tyo stream operation 120, 124, 126
: type method 133
:typeout-execute blip 88-89
: untyi stream operation 120,125
: version
make-system option 168
method 133
: which-operations
method 3 1
stream operation 120
: who- line -documentation-
string method 89
-[ format directive 140
228
INDEX
~ { format directive 140
Abort key 7, 51
activation in the input editor 54
active processes 45
advising functions 51
after daemon 26, 84, 88-89
allocate-resource function 162,
164
allocation, resource 161
always loop keyword 70
and
loop keyword 70
special form 62
append loop keyword 69
apropos function 52
arrest-reasons instance variable
45
arresting processes 45
background window 124
base-flavor-first 29
base-flavor-last 29
beep function 73
before daemon 26, 84, 89
bit-save array 47
blinker
: character 186
mouse 184-186
blip
:menu 141-143
: typeout-execute 88-89
block special form 63
boot
cold 11
warm 1 1
Boot Fep command 1 1
bp 202
bridge 214, 216
brightness, display 12
buffer
i/o 85, 89
pointer 202
shared i/o 141
bugs, reporting 52
c-A Zmacs command 222
c-Abort 7
c-B
debugger command 51
Zmacs command 222
c-D Zmacs command 222
c-E
debugger command 52
Zmacs command 222
c-Escape input editor command 53
c-F Zmacs command 222
c-Help input editor command 53
c-K
input editor command 53
Zmacs command 222
c-M debugger command 52
c-m-[ Zmacs command 222
c-m-] Zmacs command 222
c-m-A
debugger command 5 1
Zmacs command 222
c-m-Abort 7
c-m-B Zmacs command 222
c-m-E Zmacs command 222
c-m-F Zmacs command 222
c-m-K Zmacs command 222
c-m-L
debugger command 51
Zmacs command 223
c-m-R debugger command 52
c-m-Rubout Zmacs command 222
c-m-Suspend 7
c-m-Y
input editor command 53
Zmacs command 54
INDEX
229
C-N
debugger command 5 1
Zmacs command 222
c-P
debugger command 5 1
Zmacs command 222
c-R debugger command 52
c-sh-A
input editor command 54
Zmacs command 54
c-sh-C Zmacs command 223
c-sh-E Zmacs command 223
c-sh-M Zmacs command 71, 77
c-Space Zmacs command 223
c-Suspend 7
c-W
input editor command 53
Zmacs command 223
c-X
( Zmacs command 199
) Zmacs command 199
B Zmacs command 223
c-B Zmacs command 223
c-F Zmacs command 13, 223
c-S Zmacs command 13, 223
c-W Zmacs command 223
D Zmacs command 13, 224
E Zmacs command 199
c-Y
input editor command 53
Zmacs command 53, 222
canonical type 130
case in pathnames 131
catch special form 64, 76
catch-error macro 176
chaos : open- stream function 217
Chaosnet 211, 213-215
addresses 215
check-arg macro 176
check-arg-type macro 176
chosen-item instance variable 142
circular-list function 76
Clear Input
input editor command 53
Zmacs command 222
clear-resource function 165
clock function list 44
cold boot 1 1
cold-load stream 45, 50
collect loop keyword 69, 76
combined methods 23
command
Edit Namespace Object 9
extended 184, 188
Halt Machine 11
history 54
Login 9
menu 141, 143, 183
Reset Network 215
Show Documentation 8
Command Processor 8
Common Lisp 6
Compile
Buffer, M-x 223
File, M-x 223
component flavors 22, 155
comtab 183-184, 188
cond special form 61
cond-every macro 63
condition 173
simple 175
condition flavor 175
condition-bind macro 177,180
condition-bind-def ault macro
178
condition-case macro 174, 176
condition-case clause, : no-error
177
230
INDEX
conditional 61
conditions
proceeding 174-175, 177, 179
signaling and handling 173
Continue Fep command 10
continue -whopper function 27
control, flow of 61
Converse 6, 201
Copy File, M-x 127
copyf function 127
count loop keyword 69
current process 43
resetting the 46
daemon
after 26, 84, 88-89
before 26, 84, 89
dbg function 51, 59
dbg : arg function 51
dbg: loc function 51
deactivated windows 46-47
deallocate-resource function
162, 164
deal locate -whole -re source
function 165
deallocation, resource 161
debugger 5 1
command, c-B 51
command, c-E 52
command, c-M 52
command, c-m-A 51
command, c-m-L 51
command, c-m-R 52
command, c-N 51
command, c-P 51
command, c-R 52
debugging 50
deexposed windows 46
deexposed-typeout-action
instance variable 47
default
handler 178
handler for streams 120, 122, 124
pathnames 132
def const special form 36
defflavor option
:default-init-plist 87-88
: gettable-instance-
variables 19
: included-f lavors 23
: initable-instance-
variables 20
: method-combination 28
:required-init-keywords 84
: settable-instance-
variables 19
defflavor special form 18
def method special form 18
defresource option
: checker 163
: constructor 162
: finder 163
:free-list-size 164
: initial-copies 163
: initializer 162
: matcher 163
defresource macro 162
def select macro 76
defstruct
zweirbp 202
zwei:line 202
zwei: window 202
defsystem
clause, :compile-load 166
clause, ."component- systems
167
clause, : do- components 167
clause, :fasload 166
clause, : module 166
INDEX
231
clause, : package 167
clause, rpatchable 167
clause, : pathname-default
166
dependency 1 66
transformation 167
def system macro 165
def un-method macro 84
defvar
macro 84
special form 36
def whopper macro 27
def window-resource macro 170
deletef function 127
dependency, defsystem 166
describe function 21
Describe Key Bindings, M-x 184
device pathname component 131
Dictionary, Hacker's 11, 31, 34, 54,
58, 71,91, 135, 143, 167, 178
directory 128
directory pathname component
131
Dired 13
disassemble function 52
dispatch macro 63
display brightness 12
Display Font, M-x 185
do
loop keyword 68, 70
special form 63-64, 66
Document Examiner 6
dolist macro 67, 76
dotimes macro 67
edit screen menu 36, 49, 90
Edit Namespace Object com-
mand 9
Edit
Callers, M-x 52
Combined Methods, M-x 52
Extended Command, M-x 184
Key, M-x 184
Methods, M-x 52
editor, Zmacs 6, 12, 53-54, 199, 221
else loop keyword 70
End
input editor command 54
Zmacs command 224
End key 71
error
flavor 175, 179
function 175
error-output variable 124
error-restart macro 179
error-restart-loop macro 179
errset macro 176
Escape input editor command 53
essential-mouse's method for
:mouse-click 30
ethernet 213-214
Evaluate
Buffer, M-x 223
Into Buffer, M-x 206
example
graph 83
moving icons 183
tree 139
exposed windows 47
extended command 184, 188
FEP 10
Fep command
Boot 11
Continue 10
Set Chaos-address 216
Start 11
f error function 176
fibonacci numbers 74
file 125
232
INDEX
File System Maintenance 6
finally loop keyword 70, 77
finger 7
flashy scrolling 155
flavex: flavor flavor 40
flavor 1 7
condition 175
error 175, 179
f 1 a vex : f 1 a vor 40
fs:pathname 129
f s :unix-pathname 129
net: basic-host 130, 133
si : vanilla-flavor 30,84
sys: abort 179
tv: basic-mouse- sensitive-
items 88-90, 195
tv : bordered-constraint-
frame 141
tv : bordered-constraint-
f rame -with- shared- io-
buffer 141
tv:dont-select-with-
mouse-mixin 87
tv:f lashy-scrolling-mixin
155
tv: lisp-listener 17,85
tv: lisp-listener-pane 87
tv:pane-mixin 87
tv:pane-no-mouse-select-
mixin 87, 185
tv:process-mixin 88, 184
tv: sheet 47, 88
tv: stream-mixin 44
zwei: buffer 202
zwei : file-buff er 202
zwei : interval 202
zwei: interval-stream 202
zwei: node 202
zwei: top-level-node 202
zwei :window-with-comtab
184-185
Flavor Examiner 6, 33, 59
flavors
component 22, 155
mixing 22
flow of control 61
font-map instance variable 88
Font Editor 185
for loop keyword 68, 70
format directive
-[ 140
-{ 140
format function 140
frame 85
Front-End Processor 10
f s : change-file-properties
function 1 27
f s : complete -pathname function
128
f s:def ine-canonical-type func-
tion 132
fs:directory-list function 128
fs : file-properties function 127
f s : make- logical-pathname-
host function 134,171
f s : make-pathname function 1 32
fs:merge- pathnames function 132
fs:parse -pathname function 129
f s : pathname flavor 1 29
f s : set-logical-pathname-host
function 1 34
f s : unix-pathname flavor 1 29
function
♦throw 64
allocate-resource 162, 164
apropos 52
beep 73
chaos : open- stream 217
INDEX
233
circular-list 76
clear-resource 165
continue -whopper 27
copyf 127
dbg 51, 59
dbg:arg 51
dbg:loc 51
deallocate-resource 162,
164
deal locate -whole -re source
165
deletef 127
describe 21
disassemble 52
error 175
ferror 176
format 140
f s : change-file-properties
127
f s: complete-pathname 128
f s : def ine-canonical-type
132
f s:directory-list 128
f s :f ile-properties 127
f s : make-logical-pa thname-
host 134, 171
f s :make -pathname 132
f s :merge-pathnames 132
f s : parse-pathname 129
f s : set-logical-pathname-
host 134
gensym 85
grind-top-level 71
hostat 219
intern 76
load 127
load-patches 168
login 9
macroexpand 71
make-instance 19, 84
make-system 165, 167-168
map-resource 165
mapcan 66, 76
mapcar 66, 69
mapcon 66, 76
maplist 66
mexp 71, 74
nconc 66
net : f ind-paths-to-service
218
net : invoke-service-
access-path 218
net : invoke-service-on-
host 218
netirenable 215, 219
neti:reset 215, 219
open 125, 201
probef 127
process-run-function 46,
143
process-wait 44, 47
read 205
readline 204
renamef 127
rplacd 76
send 19
si : examiner-compute-
magic-list 40
si:halt 11
si:login-to-sys-host 9
si rmake-serial-stream 217
si:parse-host 218
si : set-system-source-file
168
signal 175, 180
spec 51
stream-default-handler 124
string-append 76
234
INDEX
tv : add- to- system-menu -
programs -column 188
tv:get-line-from-keyboard
90
tv: make -window 19, 48
tv: menu- choose 143
tv: mouse-set-blinker 186
tv : mouse-set-blinker-
definition 186
tv: select-or-create-
window-of -flavor 189
viewf 127
who-calls 52
zwei: command-store 201
zwei : completing-read-
from-mini-buf f er 205
zwei : find-combined-
methods 40
zwei: interval-stream 202
zwei : interval-stream-
into-bp 206
zwei :make-command-alist
188
zwei : open-editor-stream
201
zwei: read-buffer-name 205
zwei : read-def aulted-
pathname 205
zwei : set-comtab 188,201
zwei : set-comtab-
indirection 188
zwei : type in- line -
completing-read 205
zwei : typein-line-f orm-to-
eval 205
zwei :typein-line-multi-
line-read 205
zwei:typein-line-read 204
zwei :typein-line-readline
204
Function
Apropos, M-x 52
key 6, 45, 47-50; 59, 219
gensym function 85
go special form 68
graph example 83
grind-top-level function 71
grindef special form 52
Hacker's Dictionary 11, 31, 34, 54,
58, 71, 91, 135, 143, 167, 178
Halt Machine command 11
handler 173-174
default 178
restart 174, 178
Help Zmacs command 13, 221, 224
Help key 6, 184
history
command 54
input 53
kill 53
host
logical 133
physical 134
table 216
host pathname component 130
hostat 7, 219
hostat function 219
hyper-control- Function 1 1
i/o buffer 85, 89
shared 1 4 1
if macro 62
ignore variable 84
ignore-errors macro 176
inactive processes 45
inferiors instance variable 46
init keyword
:blinker-p 88
: configurations 87
: default-font 185
INDEX
235
: item-list 185
: panes 87
rprocess 88
:rows 185
: selected-pane 87
initial values for instance variables 20
initially loop keyword 70
input
editor 53
editor, activation in the 54
editor command, c-Escape 53
editor command, c-Help 53
editor command, c-K 53
editor command, c-m-Y 53
editor command, c-sh-A 54
editor command, c-W 53
editor command, c-Y 53
editor command, Clear Input
53
editor command, End 54
editor command, Escape 53
editor command, m-D 53
editor command, m-Rubout 53
editor command, m-Y 53
history 53
Inspector 6
Install Macro, M-x 200
instance 17
variable, arrest-reasons 45
variable, chosen- item 142
variable, deexposed-typeout-
action 47
variable, font-map 88
variable, inferiors 46
variable, item-list 142
variable, item-type-alist
88-89
variable, run-reasons 45
variable, superior 46
variable, tv: current-font 186
variable, tv: item-list 195
variable, x-offset 187
variable, y-offset 187
variables 1 7
variables, initial values for 20
intern function 76
Internet 211, 213-214, 216
interval 202
ip-tcp 213
item, menu 142, 185
item-list instance variable 142
item-type-alist instance variable
88-89
iteration 64
key
Abort 7, 51
End 71
Function 6, 45, 47-50, 59, 219
Help 6, 184
Local 12
Resume 7, 51
Select 6, 48-49, 87
Suspend 7, 184
keyboard 5
macro 1 99
keys, modifier 5
kill history 53
kwc-letf macro 39
let special form 69
let-globally special form 32
letf special form 39
line 202
lisp listener 8, 53
List
Callers, M-x 52
Combined Methods, M-x 37, 40,
52
Fonts, M-x 185
236
INDEX
Methods, M-x 52
Variables, M-x 184
load function 127
load-patches function 168
Local key 1 2
locks, window 50
logical
host 133
pathname 133
login function 9
Login command 9
Lookup Key Bindings, M-x 184
loop keyword
always 70
and 70
append 69
collect 69, 76
count 69
do 68, 70
else 70
finally 70, 77
for 68, 70
initially 70
maximize 69
minimize 69
nconc 69
never 70
repeat 68, 76
return 70
sum 69
thereis 70, 77
unless 70
until 70
when 70, 76
while 70
with 69
loop macro 64, 68
m- . Zmacs command 13, 71, 224
m-< Zmacs command 222
m-> Zmacs command 222
m- [ Zmacs command 222
m- ] Zmacs command 222
m-A Zmacs command 222
m-Abort 7
m-B Zmacs command 222
m-D
input editor command 53
Zmacs command 222
m-E Zmacs command 222
m-F Zmacs command 222
m-K Zmacs command 222
m-Rubout
input editor command 53
Zmacs command 222
m-sh-M Zmacs command 71, 77
m-Suspend 7, 51
m-W Zmacs command 223
M-x
Compile Buffer 223
Compile File 223
Copy File 127
Describe Key Bindings 184
Display Font 185
Edit Callers 52
Edit Combined Methods 52
Edit Extended Command 184
Edit Key 184
Edit Methods 52
Evaluate Buffer 223
Evaluate Into Buffer 206
Function Apropos 52
Install Macro 200
List Callers 52
List Combined Methods 37, 40, 52
List Fonts 185
List Methods 52
List Variables 184
Lookup Key Bindings 184
INDEX
237
Name Last Kbd Macro 200
Set Variable 184
Trace 50
m-Y
input editor command 53
Zmacs command 53-54, 222
macro 62, 71
catch-error 176
check-arg 176
check-arg-type 176
cond-every 63
condition-bind 177, 180
condition-bind-default 178
condition-case 174, 176
defresource 162
defselect 76
defsystem 165
defun-method 84
defvar 84
defwhopper 27
def window-resource 170
dispatch 63
dolist 67, 76
dotimes 67
error-restart 179
error-restart-loop 179
errset 176
if 62
ignore-errors 176
keyboard 199
kwc-letf 39
loop 64, 68
net : invoke-multiple-
services 218
select 62
selector 62
selectq 62, 76
selectq-every 63
setf 51
signal-proceed-case 180
tv: add- typeout- item-type
89, 195
typecase 63
unless 62
using-resource 162, 164
when 62
with-open-f ile 126, 201
zwei: def com 188, 201
zwei : def ine-keyboard-
macro 200
zwei: mark 203
zwei: point 203
zwei :with-editor-stream
201
macroexpand function 71
make-instance function 19, 84
make -system function 165,167-168
make-system option
: compile 167
:noconfirm 168
: print-only 168
:version 168
map-resource function 165
mapcan function 66, 76
mapcar function 66, 69
mapcon function 66, 76
maplist function 66
mapping operators 64-65
margins, window 76
maximize loop keyword 69
medium 211-212
menu 142
command 141, 143, 183
edit screen 36, 49, 90
item 142, 185
system 12, 36, 45-46, 48, 50, 188
trace 50
merging pathnames 132
238
INDEX
method 1 7
: activate 48
: alias -for- selected-
windows 87
:bury 49
.•canonical -type 133
: choose 142-143
: clear-window 76, 195
: compute-all-handlers-
once 40
: deactivate 48
rdeexpose 48-49
rdescribe 31
rdeselect 48
.•device 133
: directory 133
: draw-char 195
: draw-circle 76
: draw-line 36
: draw-lines 36
: draw-point 76
: draw-rectangle 76
: draw-triangle 76
: execute 141-143
: expose 48
: force-redisplay 202
:get 133
:get-handler-for 31
:host 133
:init 84
: inside-size 76
:item 90, 195
:kill 48
: mouse -buttons 142
: mouse-click 29,86,89-90,
143
: mouse -moves 90
: mouse-standard-blinker
186-187
name 133
new-canonical-type 133
new-device 133
new-directory 133
new-name 133
new-pathname 133
new-raw-device 133
new-raw-directory 133
new-raw-name 133
new-raw-type 133
new- type 133
new-version 133
plist 133
primitive-item 90, 195
print-self 31, 140
proceed 178, 180
proceed-type-p 180
proceed-types 180
properties 133
putprop 133
raw-device 133
raw-directory 133
raw-name 133
raw-type 133
remprop 133
report 175
reverse-video-p 76
select 48, 87
selectable-windows 87
send-if -handles 84
set-character 186
set-reverse-video-p 76
string-for-host 133
string-for-printing 133
trans lated-pathname 135
tyi 76
type 133
version 133
which-operations 31
INDEX
239
:who-line-documentation-
string 89
combination, :case 180
combination, :or 29, 89
combination, : override 89
for : mouse -click, essential-
mouse's 30
methods
combined 23
primary 25
mexp function 71, 74
mini-buffer,
pop-up 183-184
reading from the 204
minimize loop keyword 69
mixing flavors 22
modifier keys 5
more processing 50
mouse
blinker 184-186
documentation line 89
process 29, 46, 58, 86, 88, 143,
187
mouse
drag left Zmacs command 223
left Zmacs command 222
middle Zmacs command 223
mouse-sensitive items 85, 88-89
moving icons example 183
multiple windows and processes 85
Murray Hill standard utilities 52, 59
name pathname component 1 3 1
Name Last Kbd Macro, M-x 200
namespace 9,211,214,216-217
nconc
function 66
loop keyword 69
net: basic-host flavor 130,133
net : f ind-paths-to-service
function 218
net : invoke-multiple-services
macro 218
net : invoke-service-access-
path function 218
net : invoke-service-on-host
function 218
neti tenable function 215,219
neti: reset function 215, 219
network 211
never loop keyword 70
nonlocal exits 64
Notifications window 6
open option
:byte-size 126
: direction 125
:if-exists 126
open function 125, 201
or special form 62
output hold 47
package 1 2
user 12
pane 85
pathname 129
component, device 131
component, directory 131
component, host 130
component, name 131
component, type 130
component, version 130
components 1 30
logical 133
physical 134
pathnames
case in 131
default 132
merging 132
Peek 6, 45-47, 49, 219
physical
host 134
240
INDEX
pathname 134
point 202
pop-up mini-buffer 183-184
primary methods 25
prime numbers 74
probef function 127
proceed types 180
proceeding conditions 174-175, 177,
179
process 43, 85
current 43
mouse 29, 46, 58, 86, 88, 143, 187
process-run-function function
46, 143
process-wait function 44, 47
processes
active 45
arresting 45
inactive 45
multiple windows and 85
resetting 45
un-arresting 45
processing, more 50
prog special form 63, 68
protocol 211-212
race condition 86
read function 205
reading from the mini-buffer 204
readline function 204
renamef function 127
repeat loop keyword 68, 76
reporting bugs 52
Reset Network command 215
resetting
processes 45
the current process 46
resource 1 6 1
allocation 161
deallocation 161
restart handler 174, 178
Resume key 7, 51
return
loop keyword 70
special form 64, 77
return- from special form 63
rplacd function 76
Rubout Zmacs command 222
run bars 12
run-reasons instance variable 45
s-E Zmacs command 224
scheduler 43
screen 46
scrolling, flashy 155
select macro 62
Select key 6, 48-49, 87
selected windows 47
selector macro 62
selectq macro 62, 76
selectq-every macro 63
self variable 19
send function 19
serial
lines 214,217
streams 217
serial-pseudonet 214
service 211-212, 218
access path 218
futures 218
Set Chaos-address Fep command
216
Set Variable, M-x 184
setf macro 51
shared i/o buffer 141
Show Documentation command 8
si : examiner-compute-magic-
list function 40
si :halt function 11
si : login-to-sys-host function 9
INDEX
241
si :make-serial-stream function
217
si: parse-host function 218
si : set-system-source-file
function 168
si : vanilla-flavor flavor 30, 84
signal function 175, 180
signal-proceed-case macro 180
signaling and handling conditions 173
simple condition 175
site directory 134, 168, 170
spec, function 51
special form
♦catch 64
and 62
block 63
catch 64, 76
cond 61
defconst 36
def flavor 18
def method 18
defvar 36
do 63-64, 66
go 68
grindef 52
let 69
let-globally 32
letf 39
or 62
prog 63, 68
return 64, 77
return-from 63
throw 64, 76
trace 50
untrace 50
standard utilities, Murray Hill 52, 59
standard- input variable 123
standard-output variable 123
Start Fep command 1 1
stream 119
cold-load 50
operation, : characters 121
operation, : clear-input 122
operation, : clear-output 122
operation, : clear-screen 123
operation, : clear- window 123
operation, : close 122
operation, : creation-date 128
operation, : direction 121
operation, : finish 122
operation, : force-output 122
operation, : length 128
operation, : listen 121
operation, : pathname 128
operation, :read-cursorpos
122
operation, : read-pointer 123
operation, : set-cursorpos 123
operation, : set-pointer 123
operation, : string-in 121
operation, : string-out 120-121
operation, : truename 128
operation, :tyi 120, 126
operation, :tyi -no-hang 122
operation, :tyipeek 121
operation, :tyo 120, 124, 126
operation, :untyi 120, 125
operation, :which-operations
120
synonym 1 24
stream-default-handler func-
tion 124
streams, default handler for 120, 122,
124
string-append function 76
subnet 214-216
sum loop keyword 69
superior instance variable 46
242
INDEX
Suspend Zmacs command 13, 224
Suspend key 7, 184
synonym stream 124
sys : abort flavor 179
system 165
menu 12, 36, 45-46, 48, 50, 188
terminal-io variable 123
Terminal window 6
terminator 215
thereis loop keyword 70, 77
throw special form 64, 76
trace menu 50
trace special form 50
Trace, M-x 50
tracing functions 50
transceiver 214
transferring text between windows 53
transformation, defsystem 167
tree example 139
tv : add- to- system-menu-
programs-column function 188
tv:add-typeout- item- type
macro 89, 195
tv: basic-mouse- sensitive-
items flavor 88-90, 195
tv : bordered-constraint-f rame
flavor 1 4 1
tv : bordered-constraint-
frame-with- shared- io-
buf f er flavor 141
tv: current-font instance variable
186
tv:dont-select-with-mouse-
mixin flavor 87
tv:f lashy-scrolling-mixin
flavor 155
tv:get-line-from-keyboard
function 90
tv: item- list instance variable 195
tv: lisp-listener flavor 17, 85
tv: lisp-listener-pane flavor 87
tv: main- screen variable 187
tv: make -window function 19, 48
tv: menu-choose function 143
tv: mouse-process variable 46
tv: mouse-set-blinker function
186
tv: mouse -set-blinker-
definition function 186
tv : pane -mixin flavor 87
tv:pane-no-mouse-select-
mixin flavor 87, 185
tv : previously-selected-
windows variable 47, 49
tv: process -mixin flavor 88,184
tv: select-or-create-window-
of- flavor function 189
tv: selected-window variable 48
tv: sheet flavor 47, 88
tv : stream-mixin flavor 44
type pathname component 1 30
typecase macro 63
typein line 204
typeout window 13,184
un-arresting processes 45
unless
loop keyword 70
macro 62
until loop keyword 70
untrace special form 50
user package 12
using-resource macro 162, 164
variable
* 52-53
error-output 124
ignore 84
self 19
standard- input 123
INDEX
243
standard-output 123
command,
c-A 222
terminal-io 123
command,
c-B 222
tvrmain-screen 187
command,
c-D 222
tv: mouse-process 46
command,
c-E 222
tv: previously- selected-
command,
c-F 222
windows 47, 49
command,
c-K 222
tv : selected-window
48
command,
c-m-[ 222
zwei:*interval* 202
204
command,
c-m-] 222
zwei : *numeric-arg*
188
command,
c-m-A 222
zwei: * standard- comtab* 201
command,
c-m-B 222
zwei : *window* 202,204
command,
c-m-E 222
zwei : *zmacs-comtab*
201
command,
c-m-F 222
version pathname component 130
command,
c-m-K 222
viewf function 127
command,
c-m-L 223
wait-function 44
command,
c-m-Rubout 222
warm boot 1 1
command,
c-m-Y 54
when
command,
c-N 222
loop keyword 70, 76
command,
c-P 222
macro 62
command,
c-sh-A 54
while loop keyword 70
command,
c-sh-C 223
who- calls function 52
command,
c-sh-E 223
whopper 26
command,
c-sh-M 71, 77
window 46, 85
command,
c-Space 223
background 124
command,
c-W 223
locks 50
command,
c-X ( 199
margins 76
command,
c-X ) 199
windows
command,
c-X B 223
and processes, multiple 8f
command,
c-X c-B 223
deactivated 46-47
command,
c-X c-F 13, 223
deexposed 46
command,
c-X c-S 13, 223
exposed 47
command,
c-X c-W 223
selected 47
command,
c-X D 13, 224
transferring text between
53
command,
c-X E 199
with loop keyword 69
command,
c-Y 53, 222
with-open-f ile macro 126, 201
command,
Clear Input 222
wrapper 28
command,
End 224
x-of f set instance variable
187
command,
Help 13, 221, 224
y-of f set instance variable
187
command,
m-. 13,71,224
Zmacs
command,
m-< 222
244
INDEX
command, m-> 222
command, ra-[ 222
command, m-] 222
command, m-A 222
command, m-B 222
command, m-D 222
command, m-E 222
command, m-F 222
command, m-K 222
command, m-Rubout 222
command, m-sh-M 71, 77
command, m-W 223
command, m-Y 53-54, 222
command, mouse drag left
223
command, mouse left 222
command, mouse middle 223
command, Rubout 222
command, s-E 224
command, Suspend 13, 224
editor 6, 12, 53-54, 199, 221
Zmail 6, 201
zwei : *interval* variable 202,
204
zwei : *numeric-arg* variable 188
zwei : *standard-comtab* variable
201
zwei : *window* variable 202, 204
zwei : *zmacs-comtab* variable
201
zwei : bp defstruct 202
zwei : buff er flavor 202
zwei : command- store function 201
zwei : completing-read-f rom-
mini-buf f er function 205
zwei :def com macro 188,201
zwei : def ine-keyboard-macro
macro 200
zwei : file-buffer flavor 202
zwei : find-combined-methods
function 40
zwei: interval flavor 202
zwei : interval-stream
flavor 202
function 202
zwei : interval-stream-into-bp
function 206
zwei : line defstruct 202
zwei :make-command-alist func-
tion 188
zwei :mark macro 203
zwei mode flavor 202
zwei : open-editor-stream func-
tion 201
zwei : point macro 203
zwei : read-buff er-name function
205
zwei : read-def aulted-pathname
function 205
zwei : set-comtab function 188,
201
zwei : set-comtab-indirection
function 188
zwei: top-level-node flavor 202
zwei : type in- line -
completing-read function 205
zwei : typein-line-f orm-to-
eval function 205
zwei : typein-line-multi-
line-read function 205
zwei : typein-line-read function
204
zwei : typein-line-readline
function 204
zwei : window defstruct 202
zwei :window-with-comtab flavor
184-185
zwei :with-editor-stream macro
201
Please send me a copy of the examples tape that accompanies Using the
Lisp Machine. Enclosed is my check for $40.00, plus applicable sales tax,*
made payable to Symbolics, Inc. This fee includes domestic postage and
handling. (Outside North America, add $10.00 for postage.)
Ship the tape to the following street address (no PO boxes, please):
(name)
(company)
(street)
(city) (state) (zip)
( )
(phone)
♦Residents of the following states add appropriate sales tax: AZ, CA, CO. CT, FL, GA, IL. K.S. MA,
MN, NJ, NM, NY, OH, PA, TX, VA, WA.
LISP LORE: A GUIDE TO PROGRAMMING THE
LISP MACHINE is a course in programming a Lisp
machine. The book presents a readily understandable
introduction to several representative areas of interest,
including enough information to show how easy it is to
build useful programs on the Lisp machine. A graduated
series of exercises, with hints and solutions, is also
included in the text.
The book assumes some background in LISP and experi-
ence with some dialect of the language; however, no
experience with the Lisp machine itself is required.
Klu wer A cademic Publishers 0-89838-220-3